feat: kotlin android example (#226)
* feat: kotlin android example * Adding lightpush and store to kotlin example
3
.gitignore
vendored
@ -7,9 +7,10 @@ nodekey
|
|||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
*.aar
|
||||||
|
*.jar
|
||||||
|
|
||||||
# output binaries
|
# output binaries
|
||||||
main
|
|
||||||
go-waku
|
go-waku
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
|
|||||||
10
Makefile
@ -88,7 +88,7 @@ build-example-c-bindings:
|
|||||||
|
|
||||||
build-example: build-example-basic2 build-example-chat-2 build-example-filter2 build-example-c-bindings
|
build-example: build-example-basic2 build-example-chat-2 build-example-filter2 build-example-c-bindings
|
||||||
|
|
||||||
static-library: ##@cross-compile Build go-waku as static library for current platform
|
static-library:
|
||||||
@echo "Building static library..."
|
@echo "Building static library..."
|
||||||
go build \
|
go build \
|
||||||
-buildmode=c-archive \
|
-buildmode=c-archive \
|
||||||
@ -97,7 +97,7 @@ static-library: ##@cross-compile Build go-waku as static library for current pla
|
|||||||
@echo "Static library built:"
|
@echo "Static library built:"
|
||||||
@ls -la ./build/lib/libgowaku.*
|
@ls -la ./build/lib/libgowaku.*
|
||||||
|
|
||||||
dynamic-library: ##@cross-compile Build status-go as shared library for current platform
|
dynamic-library:
|
||||||
@echo "Building shared library..."
|
@echo "Building shared library..."
|
||||||
$(GOBIN_SHARED_LIB_CFLAGS) $(GOBIN_SHARED_LIB_CGO_LDFLAGS) go build \
|
$(GOBIN_SHARED_LIB_CFLAGS) $(GOBIN_SHARED_LIB_CGO_LDFLAGS) go build \
|
||||||
-buildmode=c-shared \
|
-buildmode=c-shared \
|
||||||
@ -111,3 +111,9 @@ ifeq ($(detected_OS),Linux)
|
|||||||
endif
|
endif
|
||||||
@echo "Shared library built:"
|
@echo "Shared library built:"
|
||||||
@ls -la ./build/lib/libgowaku.*
|
@ls -la ./build/lib/libgowaku.*
|
||||||
|
|
||||||
|
mobile-android:
|
||||||
|
gomobile init && \
|
||||||
|
gomobile bind -target=android -ldflags="-s -w" -o ./build/lib/gowaku.aar ./mobile
|
||||||
|
@echo "Android library built:"
|
||||||
|
@ls -la ./build/lib/*.aar ./build/lib/*.jar
|
||||||
15
examples/android-kotlin/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
3
examples/android-kotlin/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
1
examples/android-kotlin/.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
Waku
|
||||||
6
examples/android-kotlin/.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="11" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
20
examples/android-kotlin/.idea/gradle.xml
generated
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="GRADLE" />
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
23
examples/android-kotlin/.idea/misc.xml
generated
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DesignSurface">
|
||||||
|
<option name="filePathToZoomLevelMap">
|
||||||
|
<map>
|
||||||
|
<entry key="app/src/main/res/layout/activity_main.xml" value="0.35625" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
<component name="VisualizationToolProject">
|
||||||
|
<option name="state">
|
||||||
|
<ProjectState>
|
||||||
|
<option name="scale" value="0.35625" />
|
||||||
|
</ProjectState>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
examples/android-kotlin/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
36
examples/android-kotlin/README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Android Kotlin Example
|
||||||
|
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- Android Studio
|
||||||
|
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
These instructions should be executed in the terminal:
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://github.com/status-im/go-waku.git
|
||||||
|
cd go-waku
|
||||||
|
|
||||||
|
# Set required env variables
|
||||||
|
export ANDROID_NDK_HOME=/path/to/android/ndk
|
||||||
|
export ANDROID_HOME=/path/to/android/sdk/
|
||||||
|
|
||||||
|
# Build the .jar
|
||||||
|
make mobile-android
|
||||||
|
|
||||||
|
# Copy the jar into `libs/` folder
|
||||||
|
cp ./build/lib/gowaku.jar ./examples/android-kotlin/app/libs/.
|
||||||
|
```
|
||||||
|
|
||||||
|
Open the project in Android Studio and run the example app.
|
||||||
|
|
||||||
|
|
||||||
|
## Help wanted!
|
||||||
|
- Is it possible to build go-waku automatically by executing `make mobile-android` and copying the .jar automatically into `libs/` in Android Studio?
|
||||||
|
- Permissions should be requested on runtime
|
||||||
|
- Determine the required permission to fix this:
|
||||||
|
```
|
||||||
|
2022-04-07 19:29:27.542 20042-20068/com.example.waku E/GoLog: 2022-04-07T23:29:27.542Z ERROR basichost basic/basic_host.go:327 failed to resolve local interface addresses {"error": "route ip+net: netlinkrib: permission denied"}
|
||||||
|
```
|
||||||
|
- The example app blocks the main thread and code in general could be improved
|
||||||
1
examples/android-kotlin/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
45
examples/android-kotlin/app/build.gradle
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
plugins {
|
||||||
|
id 'com.android.application'
|
||||||
|
id 'org.jetbrains.kotlin.android'
|
||||||
|
id 'org.jetbrains.kotlin.plugin.serialization'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk 32
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.example.waku"
|
||||||
|
minSdk 26
|
||||||
|
targetSdk 32
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.aar'], dir: 'libs')
|
||||||
|
implementation 'androidx.core:core-ktx:1.7.0'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||||
|
implementation 'com.google.android.material:material:1.5.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
|
||||||
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
}
|
||||||
21
examples/android-kotlin/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("com.example.waku", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
examples/android-kotlin/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.waku">
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.Waku">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
<uses-permission-sdk-23 android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission-sdk-23 android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission-sdk-23 android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
|
||||||
|
<uses-permission-sdk-23 android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||||
|
</manifest>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Config(
|
||||||
|
var host: String? = null,
|
||||||
|
var result: Int? = null,
|
||||||
|
var advertiseAddr: String? = null,
|
||||||
|
var nodeKey: String? = null,
|
||||||
|
var keepAliveInterval: Int? = null,
|
||||||
|
var relay: Boolean? = null,
|
||||||
|
var minPeersToPublish: Int? = null
|
||||||
|
)
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class JsonResult<T>(val error: String? = null, val result: T? = null)
|
||||||
|
|
||||||
|
inline fun <reified T> handleResponse(response: String): T {
|
||||||
|
val jsonResult = Json.decodeFromString<JsonResult<T>>(response)
|
||||||
|
|
||||||
|
if (jsonResult.error != null)
|
||||||
|
throw Exception(jsonResult.error)
|
||||||
|
|
||||||
|
if (jsonResult.result == null)
|
||||||
|
throw Exception("no result in response")
|
||||||
|
|
||||||
|
return jsonResult.result
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun handleResponse(response: String) {
|
||||||
|
val jsonResult = Json.decodeFromString<JsonResult<String>>(response)
|
||||||
|
|
||||||
|
if (jsonResult.error != null)
|
||||||
|
throw Exception(jsonResult.error)
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.example.waku.events.Event
|
||||||
|
import com.example.waku.events.EventHandler
|
||||||
|
import com.example.waku.events.EventType
|
||||||
|
import com.example.waku.events.MessageEvent
|
||||||
|
import com.example.waku.messages.Message
|
||||||
|
import com.example.waku.messages.decodeAsymmetric
|
||||||
|
import gowaku.Gowaku.defaultPubsubTopic
|
||||||
|
|
||||||
|
val alicePrivKey: String = "0x4f012057e1a1458ce34189cb27daedbbe434f3df0825c1949475dec786e2c64e"
|
||||||
|
val alicePubKey: String =
|
||||||
|
"0x0440f05847c4c7166f57ae8ecaaf72d31bddcbca345e26713ca9e26c93fb8362ddcd5ae7f4533ee956428ad08a89cd18b234c2911a3b1c7fbd1c0047610d987302"
|
||||||
|
val bobPrivKey: String = "0xb91d6b2df8fb6ef8b53b51b2b30a408c49d5e2b530502d58ac8f94e5c5de1453"
|
||||||
|
val bobPubKey: String =
|
||||||
|
"0x045eef61a98ba1cf44a2736fac91183ea2bd86e67de20fe4bff467a71249a8a0c05f795dd7f28ced7c15eaa69c89d4212cc4f526ca5e9a62e88008f506d850cccd"
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
|
val lbl = findViewById<TextView>(R.id.lbl)
|
||||||
|
|
||||||
|
// This configuration and its attributes are optional
|
||||||
|
var c = Config()
|
||||||
|
c.relay = true
|
||||||
|
|
||||||
|
var node = Node(c)
|
||||||
|
|
||||||
|
// A callback must be registered to receive events
|
||||||
|
class MyEventHandler(var lbl: TextView) : EventHandler {
|
||||||
|
override fun handleEvent(evt: Event) {
|
||||||
|
lbl.text =
|
||||||
|
(lbl.text.toString() + ">>> Received a signal: " + evt.type.toString() + "\n")
|
||||||
|
if (evt.type == EventType.Message) {
|
||||||
|
val m = evt as MessageEvent
|
||||||
|
val decodedPayload = m.event.wakuMessage.decodeAsymmetric(bobPrivKey)
|
||||||
|
lbl.text =
|
||||||
|
(lbl.text.toString() + ">>> Message: " + decodedPayload.data.toString(
|
||||||
|
Charsets.UTF_8
|
||||||
|
) + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.setEventHandler(MyEventHandler(lbl))
|
||||||
|
|
||||||
|
node.start()
|
||||||
|
|
||||||
|
lbl.text = (lbl.text.toString() + ">>> The node peer ID is " + node.peerID() + "\n")
|
||||||
|
|
||||||
|
node.listenAddresses().forEach {
|
||||||
|
lbl.text = (lbl.text.toString() + ">>> Listening on " + it + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
lbl.text = (lbl.text.toString() + ">>> Default pubsub topic: " + defaultPubsubTopic() + "\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
node.connect("/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS")
|
||||||
|
lbl.text = (lbl.text.toString() + ">>> Connected to Peer" + "\n")
|
||||||
|
|
||||||
|
node.peers().forEach {
|
||||||
|
lbl.text = (lbl.text.toString() + ">>> Peer: " + it.peerID + "\n")
|
||||||
|
lbl.text =
|
||||||
|
(lbl.text.toString() + ">>> Protocols: " + it.protocols.joinToString(",") + "\n")
|
||||||
|
lbl.text =
|
||||||
|
(lbl.text.toString() + ">>> Addresses: " + it.addrs.joinToString(",") + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*var q = StoreQuery();
|
||||||
|
q.pubsubTopic = defaultPubsubTopic();
|
||||||
|
q.pagingOptions = new(3, null, false);
|
||||||
|
val response = node.StoreQuery(q);
|
||||||
|
println(">>> Retrieved " + response.messages.Count + " messages from store");*/
|
||||||
|
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
lbl.text = (lbl.text.toString() + ">>> Could not connect to peer: " + ex.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.relaySubscribe()
|
||||||
|
|
||||||
|
for (i in 1..5) {
|
||||||
|
val payload = ("Hello world! - " + i).toByteArray(Charsets.UTF_8)
|
||||||
|
val timestamp = System.currentTimeMillis() * 1000000
|
||||||
|
val contentTopic = ContentTopic("example", 1, "example", "rfc26")
|
||||||
|
val msg = Message(payload, contentTopic, timestamp = timestamp)
|
||||||
|
val messageID = node.relayPublishEncodeAsymmetric(msg, bobPubKey, alicePrivKey)
|
||||||
|
Thread.sleep(1_000)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.relayUnsubscribe()
|
||||||
|
|
||||||
|
node.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,326 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import com.example.waku.events.BaseEvent
|
||||||
|
import com.example.waku.events.EventHandler
|
||||||
|
import com.example.waku.events.EventType
|
||||||
|
import com.example.waku.events.MessageEvent
|
||||||
|
import com.example.waku.messages.Message
|
||||||
|
import com.example.waku.store.StoreQuery
|
||||||
|
import com.example.waku.store.StoreResponse
|
||||||
|
import gowaku.Gowaku
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param c Config containing the options used to initialize a node. It can be `null` to use
|
||||||
|
* defaults. All the keys from the configuration are optional
|
||||||
|
*/
|
||||||
|
class Node(c: Config? = null) {
|
||||||
|
var running: Boolean = false
|
||||||
|
lateinit var signalHandler: gowaku.SignalHandler
|
||||||
|
lateinit var eventHandler: EventHandler
|
||||||
|
|
||||||
|
init {
|
||||||
|
val configJson = Json.encodeToString(c)
|
||||||
|
val response = Gowaku.newNode(configJson)
|
||||||
|
handleResponse(response)
|
||||||
|
|
||||||
|
signalHandler = DefaultEventHandler()
|
||||||
|
Gowaku.setMobileSignalHandler(signalHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class DefaultEventHandler : gowaku.SignalHandler {
|
||||||
|
override fun handleSignal(signalJson: String) {
|
||||||
|
if (eventHandler != null) {
|
||||||
|
val evt = Json {
|
||||||
|
ignoreUnknownKeys = true; coerceInputValues = true
|
||||||
|
}.decodeFromString<BaseEvent>(signalJson)
|
||||||
|
when (evt.type) {
|
||||||
|
EventType.Message -> {
|
||||||
|
try {
|
||||||
|
val msgEvt = Json.decodeFromString<MessageEvent>(signalJson)
|
||||||
|
eventHandler.handleEvent(msgEvt)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// TODO: do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// TODO: do something with invalid message type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register callback to act as event handler and receive application signals which are used to
|
||||||
|
* react to asyncronous events in waku.
|
||||||
|
* @param handler event handler
|
||||||
|
*/
|
||||||
|
fun Node.setEventHandler(handler: EventHandler) {
|
||||||
|
eventHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a node mounting all the protocols that were enabled during the node instantiation.
|
||||||
|
*/
|
||||||
|
fun Node.start() {
|
||||||
|
if (running) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = Gowaku.start()
|
||||||
|
handleResponse(response)
|
||||||
|
running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops a node
|
||||||
|
*/
|
||||||
|
fun Node.stop() {
|
||||||
|
if (!running) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = Gowaku.stop()
|
||||||
|
handleResponse(response)
|
||||||
|
running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the peer ID of the go-waku node.
|
||||||
|
* @return The base58 encoded peer Id
|
||||||
|
*/
|
||||||
|
fun Node.peerID(): String {
|
||||||
|
val response = Gowaku.peerID()
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain number of connected peers
|
||||||
|
* @return The number of peers connected to this node
|
||||||
|
*/
|
||||||
|
fun Node.peerCnt(): Int {
|
||||||
|
val response = Gowaku.peerCnt()
|
||||||
|
return handleResponse<Int>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the multiaddresses the wakunode is listening to
|
||||||
|
* @return List of multiaddresses
|
||||||
|
*/
|
||||||
|
fun Node.listenAddresses(): List<String> {
|
||||||
|
val response = Gowaku.listenAddresses()
|
||||||
|
return handleResponse<List<String>>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add node multiaddress and protocol to the wakunode peerstore
|
||||||
|
* @param address multiaddress of the peer being added
|
||||||
|
* @param protocolID protocol supported by the peer
|
||||||
|
* @return Base58 encoded peer Id
|
||||||
|
*/
|
||||||
|
fun Node.addPeer(address: String, protocolID: String): String {
|
||||||
|
val response = Gowaku.addPeer(address, protocolID)
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to peer at multiaddress
|
||||||
|
* @param address multiaddress of the peer being dialed
|
||||||
|
* @param ms max duration in milliseconds this function might take to execute. If the function
|
||||||
|
* execution takes longer than this value, the execution will be canceled and an error
|
||||||
|
* returned. Use 0 for unlimited duration
|
||||||
|
*/
|
||||||
|
fun Node.connect(address: String, ms: Long = 0) {
|
||||||
|
val response = Gowaku.connect(address, ms)
|
||||||
|
handleResponse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close connection to a known peer by peerID
|
||||||
|
* @param peerID Base58 encoded peer ID to disconnect
|
||||||
|
*/
|
||||||
|
fun Node.disconnect(peerID: String) {
|
||||||
|
val response = Gowaku.disconnect(peerID)
|
||||||
|
handleResponse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to a WakuRelay topic to receive messages
|
||||||
|
* @param topic Pubsub topic to subscribe to. Use NULL for subscribing to the default pubsub topic
|
||||||
|
*/
|
||||||
|
fun Node.relaySubscribe(topic: String? = null) {
|
||||||
|
val response = Gowaku.relaySubscribe(topic)
|
||||||
|
handleResponse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish a message using waku relay
|
||||||
|
* @param msg Message to broadcast
|
||||||
|
* @param topic Pubsub topic. Set to `null` to use the default pubsub topic
|
||||||
|
* @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
|
* (in milliseconds) is reached, or an error will be returned
|
||||||
|
* @return message id
|
||||||
|
*/
|
||||||
|
fun Node.relayPublish(msg: Message, topic: String? = null, ms: Long = 0): String {
|
||||||
|
val jsonMsg = Json.encodeToString(msg)
|
||||||
|
val response = Gowaku.relayPublish(jsonMsg, topic, ms)
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish a message using waku lightpush
|
||||||
|
* @param msg Message to broadcast
|
||||||
|
* @param topic Pubsub topic. Set to `null` to use the default pubsub topic
|
||||||
|
* @param peerID ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node
|
||||||
|
* @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
|
* (in milliseconds) is reached, or an error will be returned
|
||||||
|
* @return message id
|
||||||
|
*/
|
||||||
|
fun Node.lightpushPublish(msg: Message, topic: String? = null, peerID: String? = null, ms: Long = 0): String {
|
||||||
|
val jsonMsg = Json.encodeToString(msg)
|
||||||
|
val response = Gowaku.lightpushPublish(jsonMsg, topic, peerID, ms)
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish a message encrypted with an secp256k1 public key using waku relay
|
||||||
|
* @param msg Message to broadcast
|
||||||
|
* @param publicKey Secp256k1 public key
|
||||||
|
* @param optionalSigningKey Optional secp256k1 private key for signing the message
|
||||||
|
* @param topic Pubsub topic. Set to `null` to use the default pubsub topic
|
||||||
|
* @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
|
* (in milliseconds) is reached, or an error will be returned
|
||||||
|
* @return message id
|
||||||
|
*/
|
||||||
|
fun Node.relayPublishEncodeAsymmetric(
|
||||||
|
msg: Message,
|
||||||
|
publicKey: String,
|
||||||
|
optionalSigningKey: String? = null,
|
||||||
|
topic: String? = null,
|
||||||
|
ms: Long = 0
|
||||||
|
): String {
|
||||||
|
val jsonMsg = Json.encodeToString(msg)
|
||||||
|
val response =
|
||||||
|
Gowaku.relayPublishEncodeAsymmetric(jsonMsg, topic, publicKey, optionalSigningKey, ms)
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish a message encrypted with an secp256k1 public key using waku lightpush
|
||||||
|
* @param msg Message to broadcast
|
||||||
|
* @param publicKey Secp256k1 public key
|
||||||
|
* @param optionalSigningKey Optional secp256k1 private key for signing the message
|
||||||
|
* @param topic Pubsub topic. Set to `null` to use the default pubsub topic
|
||||||
|
* @param peerID ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node
|
||||||
|
* @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
|
* (in milliseconds) is reached, or an error will be returned
|
||||||
|
* @return message id
|
||||||
|
*/
|
||||||
|
fun Node.lightpushPublishEncodeAsymmetric(
|
||||||
|
msg: Message,
|
||||||
|
publicKey: String,
|
||||||
|
optionalSigningKey: String? = null,
|
||||||
|
topic: String? = null,
|
||||||
|
peerID: String? = null,
|
||||||
|
ms: Long = 0
|
||||||
|
): String {
|
||||||
|
val jsonMsg = Json.encodeToString(msg)
|
||||||
|
val response =
|
||||||
|
Gowaku.lightpushPublishEncodeAsymmetric(jsonMsg, topic, peerID, publicKey, optionalSigningKey, ms)
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish a message encrypted with a 32 byte symmetric key using waku relay
|
||||||
|
* @param msg Message to broadcast
|
||||||
|
* @param symmetricKey 32 byte hex string containing a symmetric key
|
||||||
|
* @param optionalSigningKey Optional secp256k1 private key for signing the message
|
||||||
|
* @param topic Pubsub topic. Set to `null` to use the default pubsub topic
|
||||||
|
* @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
|
* (in milliseconds) is reached, or an error will be returned
|
||||||
|
* @return message id
|
||||||
|
*/
|
||||||
|
fun Node.relayPublishEncodeSymmetric(
|
||||||
|
msg: Message,
|
||||||
|
symmetricKey: String,
|
||||||
|
optionalSigningKey: String? = null,
|
||||||
|
topic: String? = null,
|
||||||
|
ms: Long = 0
|
||||||
|
): String {
|
||||||
|
val jsonMsg = Json.encodeToString(msg)
|
||||||
|
val response =
|
||||||
|
Gowaku.relayPublishEncodeSymmetric(jsonMsg, topic, symmetricKey, optionalSigningKey, ms)
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish a message encrypted with a 32 byte symmetric key using waku lightpush
|
||||||
|
* @param msg Message to broadcast
|
||||||
|
* @param symmetricKey 32 byte hex string containing a symmetric key
|
||||||
|
* @param optionalSigningKey Optional secp256k1 private key for signing the message
|
||||||
|
* @param topic Pubsub topic. Set to `null` to use the default pubsub topic
|
||||||
|
* @param peerID ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node
|
||||||
|
* @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
|
* (in milliseconds) is reached, or an error will be returned
|
||||||
|
* @return message id
|
||||||
|
*/
|
||||||
|
fun Node.lightpushPublishEncodeSymmetric(
|
||||||
|
msg: Message,
|
||||||
|
symmetricKey: String,
|
||||||
|
optionalSigningKey: String? = null,
|
||||||
|
topic: String? = null,
|
||||||
|
peerID: String? = null,
|
||||||
|
ms: Long = 0
|
||||||
|
): String {
|
||||||
|
val jsonMsg = Json.encodeToString(msg)
|
||||||
|
val response =
|
||||||
|
Gowaku.lightpushPublishEncodeSymmetric(jsonMsg, topic, peerID, symmetricKey, optionalSigningKey, ms)
|
||||||
|
return handleResponse<String>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if there are enough peers to publish a message on a topic
|
||||||
|
* @param topic pubsub topic to verify. Use NULL to verify the number of peers in the default pubsub topic
|
||||||
|
* @return boolean indicating if there are enough peers or not
|
||||||
|
*/
|
||||||
|
fun Node.relayEnoughPeers(topic: String? = null): Boolean {
|
||||||
|
val response = Gowaku.relayEnoughPeers(topic)
|
||||||
|
return handleResponse<Boolean>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the pubsub subscription to a pubsub topic
|
||||||
|
* @param topic Pubsub topic to unsubscribe. Use NULL for unsubscribe from the default pubsub topic
|
||||||
|
*/
|
||||||
|
fun Node.relayUnsubscribe(topic: String? = null) {
|
||||||
|
val response = Gowaku.relayUnsubscribe(topic)
|
||||||
|
handleResponse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get peers
|
||||||
|
* @return Retrieve list of peers and their supported protocols
|
||||||
|
*/
|
||||||
|
fun Node.peers(): List<Peer> {
|
||||||
|
val response = Gowaku.peers()
|
||||||
|
return handleResponse<List<Peer>>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query message history
|
||||||
|
* @param query Query
|
||||||
|
* @param peerID PeerID to ask the history from. Use NULL to automatically select a peer
|
||||||
|
* @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
|
* (in milliseconds) is reached, or an error will be returned
|
||||||
|
* @return Response containing the messages and cursor for pagination. Use the cursor in further queries to retrieve more results
|
||||||
|
*/
|
||||||
|
fun Node.storeQuery(query: StoreQuery, peerID: String?, ms: Long = 0): StoreResponse{
|
||||||
|
val queryJSON = Json.encodeToString(query)
|
||||||
|
val response = Gowaku.storeQuery(queryJSON, peerID, ms)
|
||||||
|
return handleResponse<StoreResponse>(response)
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Peer(
|
||||||
|
val peerID: String?,
|
||||||
|
val connected: Boolean,
|
||||||
|
val protocols: List<String>,
|
||||||
|
val addrs: List<String>,
|
||||||
|
)
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import gowaku.Gowaku
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get default pubsub topic
|
||||||
|
* @return Default pubsub topic used for exchanging waku messages defined in RFC 10
|
||||||
|
*/
|
||||||
|
fun DefaultPubsubTopic(): String {
|
||||||
|
return Gowaku.defaultPubsubTopic()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a content topic string
|
||||||
|
* @param applicationName
|
||||||
|
* @param applicationVersion
|
||||||
|
* @param contentTopicName
|
||||||
|
* @param encoding
|
||||||
|
* @return Content topic string according to RFC 23
|
||||||
|
*/
|
||||||
|
fun ContentTopic(applicationName: String, applicationVersion: Long, contentTopicName: String, encoding: String): String{
|
||||||
|
return Gowaku.contentTopic(applicationName, applicationVersion, contentTopicName, encoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a pubsub topic string
|
||||||
|
* @param name
|
||||||
|
* @param encoding
|
||||||
|
* @return Pubsub topic string according to RFC 23
|
||||||
|
*/
|
||||||
|
fun PubsubTopic(name: String, encoding: String): String {
|
||||||
|
return Gowaku.pubsubTopic(name, encoding)
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.example.waku.events
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class BaseEvent(override val type: EventType) : Event
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.example.waku.events
|
||||||
|
|
||||||
|
interface Event {
|
||||||
|
val type: EventType
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.example.waku.events
|
||||||
|
|
||||||
|
interface EventHandler {
|
||||||
|
fun handleEvent(evt: Event)
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.waku.events
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class EventType {
|
||||||
|
@SerialName("unknown")
|
||||||
|
Unknown,
|
||||||
|
@SerialName("message")
|
||||||
|
Message
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.example.waku.events
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class MessageEvent(override val type: EventType, val event: MessageEventData) : Event
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.example.waku.events
|
||||||
|
|
||||||
|
import com.example.waku.messages.Message
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class MessageEventData(val messageID: String, val pubsubTopic: String, val wakuMessage: Message)
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.waku.messages
|
||||||
|
|
||||||
|
import com.example.waku.serializers.ByteArrayBase64Serializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class DecodedPayload(
|
||||||
|
val pubkey: String?,
|
||||||
|
val signature: String?,
|
||||||
|
@Serializable(with = ByteArrayBase64Serializer::class) val data: ByteArray,
|
||||||
|
val padding: String?
|
||||||
|
)
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package com.example.waku.messages
|
||||||
|
|
||||||
|
import com.example.waku.handleResponse
|
||||||
|
import com.example.waku.serializers.ByteArrayBase64Serializer
|
||||||
|
import gowaku.Gowaku
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Message(
|
||||||
|
@Serializable(with = ByteArrayBase64Serializer::class) val payload: ByteArray,
|
||||||
|
val contentTopic: String? = "",
|
||||||
|
val version: Int? = 0,
|
||||||
|
val timestamp: Long? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a waku message using an asymmetric key
|
||||||
|
* @param msg Message to decode
|
||||||
|
* @param privateKey Secp256k1 private key used to decode the message
|
||||||
|
* @return DecodedPayload containing the decrypted message, padding, public key and signature (if available)
|
||||||
|
*/
|
||||||
|
fun Message.decodeAsymmetric(privateKey: String): DecodedPayload {
|
||||||
|
val jsonMsg = Json.encodeToString(this)
|
||||||
|
val response = Gowaku.decodeAsymmetric(jsonMsg, privateKey)
|
||||||
|
return handleResponse<DecodedPayload>(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a waku message using a symmetric key
|
||||||
|
* @param msg Message to decode
|
||||||
|
* @param privateKey Symmetric key used to decode the message
|
||||||
|
* @return DecodedPayload containing the decrypted message, padding, public key and signature (if available)
|
||||||
|
*/
|
||||||
|
fun Message.decodeSymmetric(symmetricKey: String): DecodedPayload {
|
||||||
|
val jsonMsg = Json.encodeToString(this)
|
||||||
|
val response = Gowaku.decodeSymmetric(jsonMsg, symmetricKey)
|
||||||
|
return handleResponse<DecodedPayload>(response)
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.example.waku.serializers
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Serializer(forClass = ByteArray::class)
|
||||||
|
object ByteArrayBase64Serializer : KSerializer<ByteArray> {
|
||||||
|
override val descriptor: SerialDescriptor =
|
||||||
|
PrimitiveSerialDescriptor("ByteArrayBase64Serializer", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: ByteArray) {
|
||||||
|
encoder.encodeString(Base64.getEncoder().encodeToString(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): ByteArray {
|
||||||
|
val text = decoder.decodeString()
|
||||||
|
return Base64.getDecoder().decode(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.example.waku.store
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ContentFilter(val contentTopic: String)
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.example.waku.store
|
||||||
|
|
||||||
|
import com.example.waku.DefaultPubsubTopic
|
||||||
|
import com.example.waku.serializers.ByteArrayBase64Serializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Cursor(
|
||||||
|
@Serializable(with = ByteArrayBase64Serializer::class) val digest: ByteArray,
|
||||||
|
val pubsubTopic: String = DefaultPubsubTopic(),
|
||||||
|
val receiverTime: Long = 0,
|
||||||
|
val senderTime: Long = 0
|
||||||
|
)
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.example.waku.store
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PagingOptions(val pageSize: Int, val cursor: Cursor?, val forward: Boolean)
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.example.waku.store
|
||||||
|
|
||||||
|
import com.example.waku.DefaultPubsubTopic
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StoreQuery(
|
||||||
|
var pubsubTopic: String? = DefaultPubsubTopic(),
|
||||||
|
var startTime: Long? = null,
|
||||||
|
var endTime: Long? = null,
|
||||||
|
var contentFilter: List<ContentFilter>?,
|
||||||
|
var pagingOptions: PagingOptions?
|
||||||
|
)
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.example.waku.store
|
||||||
|
|
||||||
|
import com.example.waku.messages.Message
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StoreResponse(val messages: List<Message>, val pagingOptions: PagingOptions?)
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/lbl"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Hello World!"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
@ -0,0 +1,16 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.Waku" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/purple_200</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
<item name="colorOnPrimary">@color/black</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/teal_200</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||||
|
<item name="colorOnSecondary">@color/black</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
10
examples/android-kotlin/app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
</resources>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Waku</string>
|
||||||
|
</resources>
|
||||||
16
examples/android-kotlin/app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.Waku" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/purple_500</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
<item name="colorOnPrimary">@color/white</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/teal_200</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||||
|
<item name="colorOnSecondary">@color/black</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package com.example.waku
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
14
examples/android-kotlin/build.gradle
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
plugins {
|
||||||
|
id 'com.android.application' version '7.1.0' apply false
|
||||||
|
id 'com.android.library' version '7.1.0' apply false
|
||||||
|
id 'org.jetbrains.kotlin.android' version '1.5.30' apply false
|
||||||
|
id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.20'
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
}
|
||||||
23
examples/android-kotlin/gradle.properties
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app"s APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
|
kotlin.code.style=official
|
||||||
|
# Enables namespacing of each library's R class so that its R class includes only the
|
||||||
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
|
# thereby reducing the size of the R class for that library
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
6
examples/android-kotlin/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Tue Apr 05 10:08:00 AST 2022
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
185
examples/android-kotlin/gradlew
vendored
Executable file
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
examples/android-kotlin/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
16
examples/android-kotlin/settings.gradle
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootProject.name = "Waku"
|
||||||
|
include ':app'
|
||||||
2
examples/c-bindings/build/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
@ -23,7 +23,7 @@ void SignalHandler(Waku.Event evt)
|
|||||||
if (evt.type == Waku.EventType.Message)
|
if (evt.type == Waku.EventType.Message)
|
||||||
{
|
{
|
||||||
Waku.MessageEvent msgEvt = (Waku.MessageEvent)evt; // Downcast to specific event type to access the event data
|
Waku.MessageEvent msgEvt = (Waku.MessageEvent)evt; // Downcast to specific event type to access the event data
|
||||||
Waku.DecodedPayload decodedPayload = node.RelayPublishDecodeAsymmetric(msgEvt.data.wakuMessage, bobPrivKey);
|
Waku.DecodedPayload decodedPayload = node.DecodeAsymmetric(msgEvt.data.wakuMessage, bobPrivKey);
|
||||||
|
|
||||||
string message = Encoding.UTF8.GetString(decodedPayload.data);
|
string message = Encoding.UTF8.GetString(decodedPayload.data);
|
||||||
Console.WriteLine(">>> Message: " + message + " from: " + decodedPayload.pubkey);
|
Console.WriteLine(">>> Message: " + message + " from: " + decodedPayload.pubkey);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ namespace Waku
|
|||||||
public string? nodeKey { get; set; }
|
public string? nodeKey { get; set; }
|
||||||
public int? keepAliveInterval { get; set; }
|
public int? keepAliveInterval { get; set; }
|
||||||
public bool? relay { get; set; }
|
public bool? relay { get; set; }
|
||||||
|
public int? minPeersToPublish {get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum EventType
|
public enum EventType
|
||||||
@ -45,8 +46,6 @@ namespace Waku
|
|||||||
|
|
||||||
public class Event
|
public class Event
|
||||||
{
|
{
|
||||||
public int nodeId { get; set; }
|
|
||||||
|
|
||||||
public EventType type { get; set; } = EventType.Unknown;
|
public EventType type { get; set; } = EventType.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +53,6 @@ namespace Waku
|
|||||||
{
|
{
|
||||||
public string messageID { get; set; } = "";
|
public string messageID { get; set; } = "";
|
||||||
|
|
||||||
public string subscriptionID { get; set; } = "";
|
|
||||||
|
|
||||||
public string pubsubTopic { get; set; } = Utils.DefaultPubsubTopic();
|
public string pubsubTopic { get; set; } = Utils.DefaultPubsubTopic();
|
||||||
|
|
||||||
public Message wakuMessage { get; set; } = new Message();
|
public Message wakuMessage { get; set; } = new Message();
|
||||||
@ -194,10 +191,14 @@ namespace Waku
|
|||||||
internal static extern IntPtr waku_start();
|
internal static extern IntPtr waku_start();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize a go-waku node mounting all the protocols that were enabled during the waku node initialization.
|
/// Initialize a go-waku node mounting all the protocols that were enabled during the waku node instantiation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
|
if(_running) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr ptr = waku_start();
|
IntPtr ptr = waku_start();
|
||||||
Response.HandleResponse(ptr);
|
Response.HandleResponse(ptr);
|
||||||
|
|
||||||
@ -212,6 +213,10 @@ namespace Waku
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
|
if(!_running) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr ptr = waku_stop();
|
IntPtr ptr = waku_stop();
|
||||||
Response.HandleResponse(ptr);
|
Response.HandleResponse(ptr);
|
||||||
|
|
||||||
@ -331,6 +336,23 @@ namespace Waku
|
|||||||
return Response.HandleResponse(ptr, "could not obtain the message id");
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DllImport(Constants.dllName)]
|
||||||
|
internal static extern IntPtr waku_lightpush_publish(string messageJSON, string? topic, string? peerID, int ms);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Publish a message using waku lightpush.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg">Message to broadcast</param>
|
||||||
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
||||||
|
/// <param name="peerID">ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node</param>
|
||||||
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string LightpushPublish(Message msg, string? topic = null, string? peerID = null, int ms = 0)
|
||||||
|
{
|
||||||
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
||||||
|
IntPtr ptr = waku_lightpush_publish(jsonMsg, topic, peerID, ms);
|
||||||
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport(Constants.dllName)]
|
[DllImport(Constants.dllName)]
|
||||||
internal static extern IntPtr waku_relay_publish_enc_asymmetric(string messageJSON, string? topic, string publicKey, string? optionalSigningKey, int ms);
|
internal static extern IntPtr waku_relay_publish_enc_asymmetric(string messageJSON, string? topic, string publicKey, string? optionalSigningKey, int ms);
|
||||||
@ -351,6 +373,26 @@ namespace Waku
|
|||||||
return Response.HandleResponse(ptr, "could not obtain the message id");
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DllImport(Constants.dllName)]
|
||||||
|
internal static extern IntPtr waku_lightpush_publish_enc_asymmetric(string messageJSON, string? topic, string? peerID, string publicKey, string? optionalSigningKey, int ms);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Publish a message encrypted with an secp256k1 public key using waku lightpush.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg">Message to broadcast</param>
|
||||||
|
/// <param name="publicKey">Secp256k1 public key</param>
|
||||||
|
/// <param name="optionalSigningKey">Optional secp256k1 private key for signing the message</param>
|
||||||
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
||||||
|
/// <param name="peerID">ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node</param>
|
||||||
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string LightpushPublishEncodeAsymmetric(Message msg, string publicKey, string? optionalSigningKey = null, string? topic = null, string? peerID = null, int ms = 0)
|
||||||
|
{
|
||||||
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
||||||
|
IntPtr ptr = waku_lightpush_publish_enc_asymmetric(jsonMsg, topic, peerID, publicKey, optionalSigningKey, ms);
|
||||||
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport(Constants.dllName)]
|
[DllImport(Constants.dllName)]
|
||||||
internal static extern IntPtr waku_relay_publish_enc_symmetric(string messageJSON, string? topic, string symmetricKey, string? optionalSigningKey, int ms);
|
internal static extern IntPtr waku_relay_publish_enc_symmetric(string messageJSON, string? topic, string symmetricKey, string? optionalSigningKey, int ms);
|
||||||
|
|
||||||
@ -370,6 +412,26 @@ namespace Waku
|
|||||||
return Response.HandleResponse(ptr, "could not obtain the message id");
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DllImport(Constants.dllName)]
|
||||||
|
internal static extern IntPtr waku_lightpush_publish_enc_symmetric(string messageJSON, string? topic, string? peerID, string symmetricKey, string? optionalSigningKey, int ms);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Publish a message encrypted with a 32 bytes symmetric key using waku lightpush.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg">Message to broadcast</param>
|
||||||
|
/// <param name="symmetricKey">32 byte hex string containing a symmetric key</param>
|
||||||
|
/// <param name="optionalSigningKey">Optional secp256k1 private key for signing the message</param>
|
||||||
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
||||||
|
/// <param name="peerID">ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node</param>
|
||||||
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string RelayPublishEncodeSymmetric(Message msg, string symmetricKey, string? optionalSigningKey = null, string? topic = null, string? peerID = null, int ms = 0)
|
||||||
|
{
|
||||||
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
||||||
|
IntPtr ptr = waku_lightpush_publish_enc_symmetric(jsonMsg, topic, peerID, symmetricKey, optionalSigningKey, ms);
|
||||||
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport(Constants.dllName)]
|
[DllImport(Constants.dllName)]
|
||||||
internal static extern IntPtr waku_decode_symmetric(string messageJSON, string symmetricKey);
|
internal static extern IntPtr waku_decode_symmetric(string messageJSON, string symmetricKey);
|
||||||
|
|
||||||
@ -379,7 +441,7 @@ namespace Waku
|
|||||||
/// <param name="msg">Message to decode</param>
|
/// <param name="msg">Message to decode</param>
|
||||||
/// <param name="symmetricKey">Symmetric key used to decode the message</param>
|
/// <param name="symmetricKey">Symmetric key used to decode the message</param>
|
||||||
/// <returns>DecodedPayload containing the decrypted message, padding, public key and signature (if available)</returns>
|
/// <returns>DecodedPayload containing the decrypted message, padding, public key and signature (if available)</returns>
|
||||||
public DecodedPayload RelayPublishDecodeSymmetric(Message msg, string symmetricKey)
|
public DecodedPayload DecodeSymmetric(Message msg, string symmetricKey)
|
||||||
{
|
{
|
||||||
string jsonMsg = JsonSerializer.Serialize(msg);
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
||||||
IntPtr ptr = waku_decode_symmetric(jsonMsg, symmetricKey);
|
IntPtr ptr = waku_decode_symmetric(jsonMsg, symmetricKey);
|
||||||
@ -395,7 +457,7 @@ namespace Waku
|
|||||||
/// <param name="msg">Message to decode</param>
|
/// <param name="msg">Message to decode</param>
|
||||||
/// <param name="privateKey">Secp256k1 private key used to decode the message</param>
|
/// <param name="privateKey">Secp256k1 private key used to decode the message</param>
|
||||||
/// <returns>DecodedPayload containing the decrypted message, padding, public key and signature (if available)</returns>
|
/// <returns>DecodedPayload containing the decrypted message, padding, public key and signature (if available)</returns>
|
||||||
public DecodedPayload RelayPublishDecodeAsymmetric(Message msg, string privateKey)
|
public DecodedPayload DecodeAsymmetric(Message msg, string privateKey)
|
||||||
{
|
{
|
||||||
string jsonMsg = JsonSerializer.Serialize(msg);
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
||||||
IntPtr ptr = waku_decode_asymmetric(jsonMsg, privateKey);
|
IntPtr ptr = waku_decode_asymmetric(jsonMsg, privateKey);
|
||||||
|
|||||||
@ -43,6 +43,7 @@ namespace Waku
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static T HandleResponse<T>(IntPtr ptr, string errNoValue) where T : struct
|
internal static T HandleResponse<T>(IntPtr ptr, string errNoValue) where T : struct
|
||||||
{
|
{
|
||||||
string strResponse = PtrToStringUtf8(ptr);
|
string strResponse = PtrToStringUtf8(ptr);
|
||||||
|
|||||||
1
go.sum
@ -1136,6 +1136,7 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI
|
|||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
|||||||
413
library/api.go
@ -6,96 +6,14 @@ package main
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
mobile "github.com/status-im/go-waku/mobile"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
|
||||||
p2pproto "github.com/libp2p/go-libp2p-core/protocol"
|
|
||||||
"github.com/multiformats/go-multiaddr"
|
|
||||||
"github.com/status-im/go-waku/waku/v2/node"
|
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol"
|
"github.com/status-im/go-waku/waku/v2/protocol"
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var wakuNode *node.WakuNode
|
|
||||||
|
|
||||||
var ErrWakuNodeNotReady = errors.New("go-waku not initialized")
|
|
||||||
|
|
||||||
func randomHex(n int) (string, error) {
|
|
||||||
bytes := make([]byte, n)
|
|
||||||
if _, err := rand.Read(bytes); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return hex.EncodeToString(bytes), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {}
|
func main() {}
|
||||||
|
|
||||||
type WakuConfig struct {
|
|
||||||
Host *string `json:"host,omitempty"`
|
|
||||||
Port *int `json:"port,omitempty"`
|
|
||||||
AdvertiseAddress *string `json:"advertiseAddr,omitempty"`
|
|
||||||
NodeKey *string `json:"nodeKey,omitempty"`
|
|
||||||
KeepAliveInterval *int `json:"keepAliveInterval,omitempty"`
|
|
||||||
EnableRelay *bool `json:"relay"`
|
|
||||||
MinPeersToPublish *int `json:"minPeersToPublish"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var DefaultHost = "0.0.0.0"
|
|
||||||
var DefaultPort = 60000
|
|
||||||
var DefaultKeepAliveInterval = 20
|
|
||||||
var DefaultEnableRelay = true
|
|
||||||
var DefaultMinPeersToPublish = 0
|
|
||||||
|
|
||||||
func getConfig(configJSON *C.char) (WakuConfig, error) {
|
|
||||||
var config WakuConfig
|
|
||||||
if configJSON != nil {
|
|
||||||
err := json.Unmarshal([]byte(C.GoString(configJSON)), &config)
|
|
||||||
if err != nil {
|
|
||||||
return WakuConfig{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Host == nil {
|
|
||||||
config.Host = &DefaultHost
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.EnableRelay == nil {
|
|
||||||
config.EnableRelay = &DefaultEnableRelay
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Host == nil {
|
|
||||||
config.Host = &DefaultHost
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Port == nil {
|
|
||||||
config.Port = &DefaultPort
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.KeepAliveInterval == nil {
|
|
||||||
config.KeepAliveInterval = &DefaultKeepAliveInterval
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.MinPeersToPublish == nil {
|
|
||||||
config.MinPeersToPublish = &DefaultMinPeersToPublish
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//export waku_new
|
//export waku_new
|
||||||
// Initialize a waku node. Receives a JSON string containing the configuration
|
// Initialize a waku node. Receives a JSON string containing the configuration
|
||||||
// for the node. It can be NULL. Example configuration:
|
// for the node. It can be NULL. Example configuration:
|
||||||
@ -112,198 +30,71 @@ func getConfig(configJSON *C.char) (WakuConfig, error) {
|
|||||||
// This function will return a nodeID which should be used in all calls from this API that require
|
// This function will return a nodeID which should be used in all calls from this API that require
|
||||||
// interacting with the node.
|
// interacting with the node.
|
||||||
func waku_new(configJSON *C.char) *C.char {
|
func waku_new(configJSON *C.char) *C.char {
|
||||||
if wakuNode != nil {
|
response := mobile.NewNode(C.GoString(configJSON))
|
||||||
return makeJSONResponse(errors.New("go-waku already initialized. stop it first"))
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
config, err := getConfig(configJSON)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", *config.Host, *config.Port))
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var prvKey *ecdsa.PrivateKey
|
|
||||||
if config.NodeKey != nil {
|
|
||||||
prvKey, err = crypto.HexToECDSA(*config.NodeKey)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
key, err := randomHex(32)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
prvKey, err = crypto.HexToECDSA(key)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := []node.WakuNodeOption{
|
|
||||||
node.WithPrivateKey(prvKey),
|
|
||||||
node.WithHostAddress(hostAddr),
|
|
||||||
node.WithKeepAlive(time.Duration(*config.KeepAliveInterval) * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
if *config.EnableRelay {
|
|
||||||
opts = append(opts, node.WithWakuRelayAndMinPeers(*config.MinPeersToPublish))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
w, err := node.New(ctx, opts...)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wakuNode = w
|
|
||||||
|
|
||||||
return makeJSONResponse(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_start
|
//export waku_start
|
||||||
// Starts the waku node
|
// Starts the waku node
|
||||||
func waku_start() *C.char {
|
func waku_start() *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.Start()
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
if err := wakuNode.Start(); err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return makeJSONResponse(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_stop
|
//export waku_stop
|
||||||
// Stops a waku node
|
// Stops a waku node
|
||||||
func waku_stop() *C.char {
|
func waku_stop() *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.Stop()
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
wakuNode.Stop()
|
|
||||||
wakuNode = nil
|
|
||||||
|
|
||||||
return makeJSONResponse(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_peerid
|
//export waku_peerid
|
||||||
// Obtain the peer ID of the waku node
|
// Obtain the peer ID of the waku node
|
||||||
func waku_peerid() *C.char {
|
func waku_peerid() *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.PeerID()
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(wakuNode.ID(), nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_listen_addresses
|
//export waku_listen_addresses
|
||||||
// Obtain the multiaddresses the wakunode is listening to
|
// Obtain the multiaddresses the wakunode is listening to
|
||||||
func waku_listen_addresses() *C.char {
|
func waku_listen_addresses() *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.ListenAddresses()
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
var addresses []string
|
|
||||||
for _, addr := range wakuNode.ListenAddresses() {
|
|
||||||
addresses = append(addresses, addr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(addresses, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_add_peer
|
//export waku_add_peer
|
||||||
// Add node multiaddress and protocol to the wakunode peerstore
|
// Add node multiaddress and protocol to the wakunode peerstore
|
||||||
func waku_add_peer(address *C.char, protocolID *C.char) *C.char {
|
func waku_add_peer(address *C.char, protocolID *C.char) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.AddPeer(C.GoString(address), C.GoString(protocolID))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
ma, err := multiaddr.NewMultiaddr(C.GoString(address))
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
peerID, err := wakuNode.AddPeer(ma, p2pproto.ID(C.GoString(protocolID)))
|
|
||||||
return prepareJSONResponse(peerID, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_connect
|
//export waku_connect
|
||||||
// Connect to peer at multiaddress. if ms > 0, cancel the function execution if it takes longer than N milliseconds
|
// Connect to peer at multiaddress. if ms > 0, cancel the function execution if it takes longer than N milliseconds
|
||||||
func waku_connect(address *C.char, ms C.int) *C.char {
|
func waku_connect(address *C.char, ms C.int) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.Connect(C.GoString(address), int(ms))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
var ctx context.Context
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
|
|
||||||
if ms > 0 {
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
} else {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
err := wakuNode.DialPeer(ctx, C.GoString(address))
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_connect_peerid
|
//export waku_connect_peerid
|
||||||
// Connect to known peer by peerID. if ms > 0, cancel the function execution if it takes longer than N milliseconds
|
// Connect to known peer by peerID. if ms > 0, cancel the function execution if it takes longer than N milliseconds
|
||||||
func waku_connect_peerid(peerID *C.char, ms C.int) *C.char {
|
func waku_connect_peerid(peerID *C.char, ms C.int) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.Connect(C.GoString(peerID), int(ms))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
var ctx context.Context
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
|
|
||||||
pID, err := peer.Decode(C.GoString(peerID))
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ms > 0 {
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
} else {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = wakuNode.DialPeerByID(ctx, pID)
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_disconnect
|
//export waku_disconnect
|
||||||
// Close connection to a known peer by peerID
|
// Close connection to a known peer by peerID
|
||||||
func waku_disconnect(peerID *C.char) *C.char {
|
func waku_disconnect(peerID *C.char) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.Disconnect(C.GoString(peerID))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
pID, err := peer.Decode(C.GoString(peerID))
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = wakuNode.ClosePeerById(pID)
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_peer_cnt
|
//export waku_peer_cnt
|
||||||
// Get number of connected peers
|
// Get number of connected peers
|
||||||
func waku_peer_cnt() *C.char {
|
func waku_peer_cnt() *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.PeerCnt()
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(wakuNode.PeerCount(), nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_content_topic
|
//export waku_content_topic
|
||||||
@ -315,23 +106,13 @@ func waku_content_topic(applicationName *C.char, applicationVersion C.uint, cont
|
|||||||
//export waku_pubsub_topic
|
//export waku_pubsub_topic
|
||||||
// Create a pubsub topic string according to RFC 23
|
// Create a pubsub topic string according to RFC 23
|
||||||
func waku_pubsub_topic(name *C.char, encoding *C.char) *C.char {
|
func waku_pubsub_topic(name *C.char, encoding *C.char) *C.char {
|
||||||
return prepareJSONResponse(protocol.NewPubsubTopic(C.GoString(name), C.GoString(encoding)).String(), nil)
|
return C.CString(mobile.PubsubTopic(C.GoString(name), C.GoString(encoding)))
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_default_pubsub_topic
|
//export waku_default_pubsub_topic
|
||||||
// Get the default pubsub topic used in waku2: /waku/2/default-waku/proto
|
// Get the default pubsub topic used in waku2: /waku/2/default-waku/proto
|
||||||
func waku_default_pubsub_topic() *C.char {
|
func waku_default_pubsub_topic() *C.char {
|
||||||
return C.CString(protocol.DefaultPubsubTopic().String())
|
return C.CString(mobile.DefaultPubsubTopic())
|
||||||
}
|
|
||||||
|
|
||||||
func getTopic(topic *C.char) string {
|
|
||||||
result := ""
|
|
||||||
if topic != nil {
|
|
||||||
result = C.GoString(topic)
|
|
||||||
} else {
|
|
||||||
result = protocol.DefaultPubsubTopic().String()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_set_event_callback
|
//export waku_set_event_callback
|
||||||
@ -339,160 +120,26 @@ func getTopic(topic *C.char) string {
|
|||||||
// (in JSON) which are used o react to asyncronous events in waku. The function
|
// (in JSON) which are used o react to asyncronous events in waku. The function
|
||||||
// signature for the callback should be `void myCallback(char* signalJSON)`
|
// signature for the callback should be `void myCallback(char* signalJSON)`
|
||||||
func waku_set_event_callback(cb unsafe.Pointer) {
|
func waku_set_event_callback(cb unsafe.Pointer) {
|
||||||
setEventCallback(cb)
|
mobile.SetEventCallback(cb)
|
||||||
}
|
|
||||||
|
|
||||||
type SubscriptionMsg struct {
|
|
||||||
MessageID string `json:"messageID"`
|
|
||||||
PubsubTopic string `json:"pubsubTopic"`
|
|
||||||
Message *pb.WakuMessage `json:"wakuMessage"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func toSubscriptionMessage(msg *protocol.Envelope) *SubscriptionMsg {
|
|
||||||
return &SubscriptionMsg{
|
|
||||||
MessageID: hexutil.Encode(msg.Hash()),
|
|
||||||
PubsubTopic: msg.PubsubTopic(),
|
|
||||||
Message: msg.Message(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_peers
|
//export waku_peers
|
||||||
// Retrieve the list of peers known by the waku node
|
// Retrieve the list of peers known by the waku node
|
||||||
func waku_peers() *C.char {
|
func waku_peers() *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.Peers()
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
peers, err := wakuNode.Peers()
|
|
||||||
return prepareJSONResponse(peers, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalPubkey(pub []byte) (ecdsa.PublicKey, error) {
|
|
||||||
x, y := elliptic.Unmarshal(secp256k1.S256(), pub)
|
|
||||||
if x == nil {
|
|
||||||
return ecdsa.PublicKey{}, errors.New("invalid public key")
|
|
||||||
}
|
|
||||||
return ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_decode_symmetric
|
//export waku_decode_symmetric
|
||||||
// Decode a waku message using a 32 bytes symmetric key. The key must be a hex encoded string with "0x" prefix
|
// Decode a waku message using a 32 bytes symmetric key. The key must be a hex encoded string with "0x" prefix
|
||||||
func waku_decode_symmetric(messageJSON *C.char, symmetricKey *C.char) *C.char {
|
func waku_decode_symmetric(messageJSON *C.char, symmetricKey *C.char) *C.char {
|
||||||
var msg pb.WakuMessage
|
response := mobile.DecodeSymmetric(C.GoString(messageJSON), C.GoString(symmetricKey))
|
||||||
err := json.Unmarshal([]byte(C.GoString(messageJSON)), &msg)
|
return C.CString(response)
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.Version == 0 {
|
|
||||||
return prepareJSONResponse(msg.Payload, nil)
|
|
||||||
} else if msg.Version > 1 {
|
|
||||||
return makeJSONResponse(errors.New("unsupported wakumessage version"))
|
|
||||||
}
|
|
||||||
|
|
||||||
keyInfo := &node.KeyInfo{
|
|
||||||
Kind: node.Symmetric,
|
|
||||||
}
|
|
||||||
|
|
||||||
keyInfo.SymKey, err = hexutil.Decode(C.GoString(symmetricKey))
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := node.DecodePayload(&msg, keyInfo)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
response := struct {
|
|
||||||
PubKey string `json:"pubkey"`
|
|
||||||
Signature string `json:"signature"`
|
|
||||||
Data []byte `json:"data"`
|
|
||||||
Padding []byte `json:"padding"`
|
|
||||||
}{
|
|
||||||
PubKey: hexutil.Encode(crypto.FromECDSAPub(payload.PubKey)),
|
|
||||||
Signature: hexutil.Encode(payload.Signature),
|
|
||||||
Data: payload.Data,
|
|
||||||
Padding: payload.Padding,
|
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(response, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_decode_asymmetric
|
//export waku_decode_asymmetric
|
||||||
// Decode a waku message using a secp256k1 private key. The key must be a hex encoded string with "0x" prefix
|
// Decode a waku message using a secp256k1 private key. The key must be a hex encoded string with "0x" prefix
|
||||||
func waku_decode_asymmetric(messageJSON *C.char, privateKey *C.char) *C.char {
|
func waku_decode_asymmetric(messageJSON *C.char, privateKey *C.char) *C.char {
|
||||||
var msg pb.WakuMessage
|
response := mobile.DecodeAsymmetric(C.GoString(messageJSON), C.GoString(privateKey))
|
||||||
err := json.Unmarshal([]byte(C.GoString(messageJSON)), &msg)
|
return C.CString(response)
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.Version == 0 {
|
|
||||||
return prepareJSONResponse(msg.Payload, nil)
|
|
||||||
} else if msg.Version > 1 {
|
|
||||||
return makeJSONResponse(errors.New("unsupported wakumessage version"))
|
|
||||||
}
|
|
||||||
|
|
||||||
keyInfo := &node.KeyInfo{
|
|
||||||
Kind: node.Asymmetric,
|
|
||||||
}
|
|
||||||
|
|
||||||
keyBytes, err := hexutil.Decode(C.GoString(privateKey))
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
keyInfo.PrivKey, err = crypto.ToECDSA(keyBytes)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := node.DecodePayload(&msg, keyInfo)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
response := struct {
|
|
||||||
PubKey string `json:"pubkey"`
|
|
||||||
Signature string `json:"signature"`
|
|
||||||
Data []byte `json:"data"`
|
|
||||||
Padding []byte `json:"padding"`
|
|
||||||
}{
|
|
||||||
PubKey: hexutil.Encode(crypto.FromECDSAPub(payload.PubKey)),
|
|
||||||
Signature: hexutil.Encode(payload.Signature),
|
|
||||||
Data: payload.Data,
|
|
||||||
Padding: payload.Padding,
|
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(response, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_utils_base64_decode
|
|
||||||
// Decode a base64 string (useful for reading the payload from waku messages)
|
|
||||||
func waku_utils_base64_decode(data *C.char) *C.char {
|
|
||||||
b, err := base64.StdEncoding.DecodeString(C.GoString(data))
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(string(b), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export waku_utils_base64_encode
|
|
||||||
// Encode data to base64 (useful for creating the payload of a waku message in the
|
|
||||||
// format understood by waku_relay_publish)
|
|
||||||
func waku_utils_base64_encode(data *C.char) *C.char {
|
|
||||||
str := base64.StdEncoding.EncodeToString([]byte(C.GoString(data)))
|
|
||||||
return C.CString(string(str))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//export waku_utils_free
|
|
||||||
// Frees a char* since all strings returned by gowaku are allocated in the C heap using malloc.
|
|
||||||
func waku_utils_free(data *C.char) {
|
|
||||||
C.free(unsafe.Pointer(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// connected/disconnected
|
|
||||||
|
|||||||
@ -3,46 +3,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"C"
|
"C"
|
||||||
|
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
mobile "github.com/status-im/go-waku/mobile"
|
||||||
)
|
)
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol/lightpush"
|
|
||||||
)
|
|
||||||
|
|
||||||
func lightpushPublish(msg pb.WakuMessage, pubsubTopic string, peerID string, ms int) (string, error) {
|
|
||||||
if wakuNode == nil {
|
|
||||||
return "", ErrWakuNodeNotReady
|
|
||||||
}
|
|
||||||
|
|
||||||
var ctx context.Context
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
|
|
||||||
if ms > 0 {
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
} else {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
var lpOptions []lightpush.LightPushOption
|
|
||||||
if peerID != "" {
|
|
||||||
p, err := peer.Decode(peerID)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
lpOptions = append(lpOptions, lightpush.WithPeer(p))
|
|
||||||
} else {
|
|
||||||
lpOptions = append(lpOptions, lightpush.WithAutomaticPeerSelection(wakuNode.Host()))
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := wakuNode.Lightpush().PublishToTopic(ctx, &msg, pubsubTopic, lpOptions...)
|
|
||||||
return hexutil.Encode(hash), err
|
|
||||||
}
|
|
||||||
|
|
||||||
//export waku_lightpush_publish
|
//export waku_lightpush_publish
|
||||||
// Publish a message using waku lightpush. Use NULL for topic to use the default pubsub topic..
|
// Publish a message using waku lightpush. Use NULL for topic to use the default pubsub topic..
|
||||||
@ -50,13 +12,8 @@ func lightpushPublish(msg pb.WakuMessage, pubsubTopic string, peerID string, ms
|
|||||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
// (in milliseconds) is reached, or an error will be returned
|
// (in milliseconds) is reached, or an error will be returned
|
||||||
func waku_lightpush_publish(messageJSON *C.char, topic *C.char, peerID *C.char, ms C.int) *C.char {
|
func waku_lightpush_publish(messageJSON *C.char, topic *C.char, peerID *C.char, ms C.int) *C.char {
|
||||||
msg, err := wakuMessage(C.GoString(messageJSON))
|
response := mobile.LightpushPublish(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), int(ms))
|
||||||
if err != nil {
|
return C.CString(response)
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := lightpushPublish(msg, getTopic(topic), C.GoString(peerID), int(ms))
|
|
||||||
return prepareJSONResponse(hash, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_lightpush_publish_enc_asymmetric
|
//export waku_lightpush_publish_enc_asymmetric
|
||||||
@ -67,14 +24,8 @@ func waku_lightpush_publish(messageJSON *C.char, topic *C.char, peerID *C.char,
|
|||||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
// (in milliseconds) is reached, or an error will be returned.
|
// (in milliseconds) is reached, or an error will be returned.
|
||||||
func waku_lightpush_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, peerID *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
func waku_lightpush_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, peerID *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
||||||
msg, err := wakuMessageAsymmetricEncoding(C.GoString(messageJSON), C.GoString(publicKey), C.GoString(optionalSigningKey))
|
response := mobile.LightpushPublishEncodeAsymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), C.GoString(publicKey), C.GoString(optionalSigningKey), int(ms))
|
||||||
if err != nil {
|
return C.CString(response)
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := lightpushPublish(msg, getTopic(topic), C.GoString(peerID), int(ms))
|
|
||||||
|
|
||||||
return prepareJSONResponse(hash, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_lightpush_publish_enc_symmetric
|
//export waku_lightpush_publish_enc_symmetric
|
||||||
@ -85,12 +36,6 @@ func waku_lightpush_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, p
|
|||||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
// (in milliseconds) is reached, or an error will be returned.
|
// (in milliseconds) is reached, or an error will be returned.
|
||||||
func waku_lightpush_publish_enc_symmetric(messageJSON *C.char, topic *C.char, peerID *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
func waku_lightpush_publish_enc_symmetric(messageJSON *C.char, topic *C.char, peerID *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
||||||
msg, err := wakuMessageSymmetricEncoding(C.GoString(messageJSON), C.GoString(symmetricKey), C.GoString(optionalSigningKey))
|
response := mobile.LightpushPublishEncodeSymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), C.GoString(symmetricKey), C.GoString(optionalSigningKey), int(ms))
|
||||||
if err != nil {
|
return C.CString(response)
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := lightpushPublish(msg, getTopic(topic), C.GoString(peerID), int(ms))
|
|
||||||
|
|
||||||
return prepareJSONResponse(hash, err)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,55 +2,16 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"C"
|
"C"
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
mobile "github.com/status-im/go-waku/mobile"
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol"
|
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
|
||||||
)
|
)
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol/relay"
|
|
||||||
)
|
|
||||||
|
|
||||||
var subscriptions map[string]*relay.Subscription = make(map[string]*relay.Subscription)
|
|
||||||
var mutex sync.Mutex
|
|
||||||
|
|
||||||
//export waku_relay_enough_peers
|
//export waku_relay_enough_peers
|
||||||
// Determine if there are enough peers to publish a message on a topic. Use NULL
|
// Determine if there are enough peers to publish a message on a topic. Use NULL
|
||||||
// to verify the number of peers in the default pubsub topic
|
// to verify the number of peers in the default pubsub topic
|
||||||
func waku_relay_enough_peers(topic *C.char) *C.char {
|
func waku_relay_enough_peers(topic *C.char) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.RelayEnoughPeers(C.GoString(topic))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
topicToCheck := protocol.DefaultPubsubTopic().String()
|
|
||||||
if topic != nil {
|
|
||||||
topicToCheck = C.GoString(topic)
|
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(wakuNode.Relay().EnoughPeersToPublishToTopic(topicToCheck), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func relayPublish(msg pb.WakuMessage, pubsubTopic string, ms int) (string, error) {
|
|
||||||
if wakuNode == nil {
|
|
||||||
return "", ErrWakuNodeNotReady
|
|
||||||
}
|
|
||||||
|
|
||||||
var ctx context.Context
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
|
|
||||||
if ms > 0 {
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
} else {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := wakuNode.Relay().PublishToTopic(ctx, &msg, pubsubTopic)
|
|
||||||
return hexutil.Encode(hash), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_relay_publish
|
//export waku_relay_publish
|
||||||
@ -58,13 +19,8 @@ func relayPublish(msg pb.WakuMessage, pubsubTopic string, ms int) (string, error
|
|||||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
// (in milliseconds) is reached, or an error will be returned
|
// (in milliseconds) is reached, or an error will be returned
|
||||||
func waku_relay_publish(messageJSON *C.char, topic *C.char, ms C.int) *C.char {
|
func waku_relay_publish(messageJSON *C.char, topic *C.char, ms C.int) *C.char {
|
||||||
msg, err := wakuMessage(C.GoString(messageJSON))
|
response := mobile.RelayPublish(C.GoString(messageJSON), C.GoString(topic), int(ms))
|
||||||
if err != nil {
|
return C.CString(response)
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := relayPublish(msg, getTopic(topic), int(ms))
|
|
||||||
return prepareJSONResponse(hash, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_relay_publish_enc_asymmetric
|
//export waku_relay_publish_enc_asymmetric
|
||||||
@ -74,14 +30,8 @@ func waku_relay_publish(messageJSON *C.char, topic *C.char, ms C.int) *C.char {
|
|||||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
// (in milliseconds) is reached, or an error will be returned.
|
// (in milliseconds) is reached, or an error will be returned.
|
||||||
func waku_relay_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
func waku_relay_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
||||||
msg, err := wakuMessageAsymmetricEncoding(C.GoString(messageJSON), C.GoString(publicKey), C.GoString(optionalSigningKey))
|
response := mobile.RelayPublishEncodeAsymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(publicKey), C.GoString(optionalSigningKey), int(ms))
|
||||||
if err != nil {
|
return C.CString(response)
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := relayPublish(msg, getTopic(topic), int(ms))
|
|
||||||
|
|
||||||
return prepareJSONResponse(hash, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_relay_publish_enc_symmetric
|
//export waku_relay_publish_enc_symmetric
|
||||||
@ -91,14 +41,8 @@ func waku_relay_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, publi
|
|||||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
// (in milliseconds) is reached, or an error will be returned.
|
// (in milliseconds) is reached, or an error will be returned.
|
||||||
func waku_relay_publish_enc_symmetric(messageJSON *C.char, topic *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
func waku_relay_publish_enc_symmetric(messageJSON *C.char, topic *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char {
|
||||||
msg, err := wakuMessageSymmetricEncoding(C.GoString(messageJSON), C.GoString(symmetricKey), C.GoString(optionalSigningKey))
|
response := mobile.RelayPublishEncodeSymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(symmetricKey), C.GoString(optionalSigningKey), int(ms))
|
||||||
if err != nil {
|
return C.CString(response)
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := relayPublish(msg, getTopic(topic), int(ms))
|
|
||||||
|
|
||||||
return prepareJSONResponse(hash, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_relay_subscribe
|
//export waku_relay_subscribe
|
||||||
@ -107,68 +51,14 @@ func waku_relay_publish_enc_symmetric(messageJSON *C.char, topic *C.char, symmet
|
|||||||
// or an error message. When a message is received, a "message" is emitted containing
|
// or an error message. When a message is received, a "message" is emitted containing
|
||||||
// the message, pubsub topic, and nodeID in which the message was received
|
// the message, pubsub topic, and nodeID in which the message was received
|
||||||
func waku_relay_subscribe(topic *C.char) *C.char {
|
func waku_relay_subscribe(topic *C.char) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.RelaySubscribe(C.GoString(topic))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
topicToSubscribe := protocol.DefaultPubsubTopic().String()
|
|
||||||
if topic != nil {
|
|
||||||
topicToSubscribe = C.GoString(topic)
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
subscription, ok := subscriptions[topicToSubscribe]
|
|
||||||
if ok {
|
|
||||||
return makeJSONResponse(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscription, err := wakuNode.Relay().SubscribeToTopic(context.Background(), topicToSubscribe)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriptions[topicToSubscribe] = subscription
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for envelope := range subscription.C {
|
|
||||||
send("message", toSubscriptionMessage(envelope))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return makeJSONResponse(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export waku_relay_unsubscribe
|
//export waku_relay_unsubscribe
|
||||||
// Closes the pubsub subscription to a pubsub topic. Existing subscriptions
|
// Closes the pubsub subscription to a pubsub topic. Existing subscriptions
|
||||||
// will not be closed, but they will stop receiving messages
|
// will not be closed, but they will stop receiving messages
|
||||||
func waku_relay_unsubscribe(topic *C.char) *C.char {
|
func waku_relay_unsubscribe(topic *C.char) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.RelayUnsubscribe(C.GoString(topic))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
topicToUnsubscribe := protocol.DefaultPubsubTopic().String()
|
|
||||||
if topic != nil {
|
|
||||||
topicToUnsubscribe = C.GoString(topic)
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
subscription, ok := subscriptions[topicToUnsubscribe]
|
|
||||||
if ok {
|
|
||||||
return makeJSONResponse(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscription.Unsubscribe()
|
|
||||||
|
|
||||||
delete(subscriptions, topicToUnsubscribe)
|
|
||||||
|
|
||||||
err := wakuNode.Relay().Unsubscribe(context.Background(), topicToUnsubscribe)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return makeJSONResponse(nil)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,37 +2,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"C"
|
"C"
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
mobile "github.com/status-im/go-waku/mobile"
|
||||||
"github.com/status-im/go-waku/waku/v2/protocol/store"
|
|
||||||
)
|
)
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StorePagingOptions struct {
|
|
||||||
PageSize uint64 `json:"pageSize,omitempty"`
|
|
||||||
Cursor *pb.Index `json:"cursor,omitempty"`
|
|
||||||
Forward bool `json:"forward,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type StoreMessagesArgs struct {
|
|
||||||
Topic string `json:"pubsubTopic,omitempty"`
|
|
||||||
ContentFilters []string `json:"contentFilters,omitempty"`
|
|
||||||
StartTime int64 `json:"startTime,omitempty"`
|
|
||||||
EndTime int64 `json:"endTime,omitempty"`
|
|
||||||
PagingOptions StorePagingOptions `json:"pagingOptions,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type StoreMessagesReply struct {
|
|
||||||
Messages []*pb.WakuMessage `json:"messages,omitempty"`
|
|
||||||
PagingInfo StorePagingOptions `json:"pagingInfo,omitempty"`
|
|
||||||
Error string `json:"error,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
//export waku_store_query
|
//export waku_store_query
|
||||||
// Query historic messages using waku store protocol.
|
// Query historic messages using waku store protocol.
|
||||||
@ -61,66 +33,6 @@ type StoreMessagesReply struct {
|
|||||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||||
// (in milliseconds) is reached, or an error will be returned
|
// (in milliseconds) is reached, or an error will be returned
|
||||||
func waku_store_query(queryJSON *C.char, peerID *C.char, ms C.int) *C.char {
|
func waku_store_query(queryJSON *C.char, peerID *C.char, ms C.int) *C.char {
|
||||||
if wakuNode == nil {
|
response := mobile.StoreQuery(C.GoString(queryJSON), C.GoString(peerID), int(ms))
|
||||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
return C.CString(response)
|
||||||
}
|
|
||||||
|
|
||||||
var args StoreMessagesArgs
|
|
||||||
err := json.Unmarshal([]byte(C.GoString(queryJSON)), &args)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
options := []store.HistoryRequestOption{
|
|
||||||
store.WithAutomaticRequestId(),
|
|
||||||
store.WithPaging(args.PagingOptions.Forward, args.PagingOptions.PageSize),
|
|
||||||
store.WithCursor(args.PagingOptions.Cursor),
|
|
||||||
}
|
|
||||||
|
|
||||||
pid := C.GoString(peerID)
|
|
||||||
if pid != "" {
|
|
||||||
p, err := peer.Decode(pid)
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
options = append(options, store.WithPeer(p))
|
|
||||||
} else {
|
|
||||||
options = append(options, store.WithAutomaticPeerSelection())
|
|
||||||
}
|
|
||||||
|
|
||||||
reply := StoreMessagesReply{}
|
|
||||||
|
|
||||||
var ctx context.Context
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
|
|
||||||
if ms > 0 {
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
} else {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := wakuNode.Store().Query(
|
|
||||||
ctx,
|
|
||||||
store.Query{
|
|
||||||
Topic: args.Topic,
|
|
||||||
ContentTopics: args.ContentFilters,
|
|
||||||
StartTime: args.StartTime,
|
|
||||||
EndTime: args.EndTime,
|
|
||||||
},
|
|
||||||
options...,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
reply.Error = err.Error()
|
|
||||||
return prepareJSONResponse(reply, nil)
|
|
||||||
}
|
|
||||||
reply.Messages = res.Messages
|
|
||||||
reply.PagingInfo = StorePagingOptions{
|
|
||||||
PageSize: args.PagingOptions.PageSize,
|
|
||||||
Cursor: res.Cursor(),
|
|
||||||
Forward: args.PagingOptions.Forward,
|
|
||||||
}
|
|
||||||
|
|
||||||
return prepareJSONResponse(reply, nil)
|
|
||||||
}
|
}
|
||||||
|
|||||||
37
library/api_utils.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//export waku_utils_base64_decode
|
||||||
|
// Decode a base64 string (useful for reading the payload from waku messages)
|
||||||
|
func waku_utils_base64_decode(data *C.char) *C.char {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(C.GoString(data))
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(string(b), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export waku_utils_base64_encode
|
||||||
|
// Encode data to base64 (useful for creating the payload of a waku message in the
|
||||||
|
// format understood by waku_relay_publish)
|
||||||
|
func waku_utils_base64_encode(data *C.char) *C.char {
|
||||||
|
str := base64.StdEncoding.EncodeToString([]byte(C.GoString(data)))
|
||||||
|
return C.CString(string(str))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//export waku_utils_free
|
||||||
|
// Frees a char* since all strings returned by gowaku are allocated in the C heap using malloc.
|
||||||
|
func waku_utils_free(data *C.char) {
|
||||||
|
C.free(unsafe.Pointer(data))
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONResponse struct {
|
type jsonResponse struct {
|
||||||
Error *string `json:"error,omitempty"`
|
Error *string `json:"error,omitempty"`
|
||||||
Result interface{} `json:"result"`
|
Result interface{} `json:"result"`
|
||||||
}
|
}
|
||||||
@ -14,14 +14,14 @@ func prepareJSONResponse(result interface{}, err error) *C.char {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errStr := err.Error()
|
errStr := err.Error()
|
||||||
errResponse := JSONResponse{
|
errResponse := jsonResponse{
|
||||||
Error: &errStr,
|
Error: &errStr,
|
||||||
}
|
}
|
||||||
response, _ := json.Marshal(&errResponse)
|
response, _ := json.Marshal(&errResponse)
|
||||||
return C.CString(string(response))
|
return C.CString(string(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(JSONResponse{Result: result})
|
data, err := json.Marshal(jsonResponse{Result: result})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return prepareJSONResponse(nil, err)
|
return prepareJSONResponse(nil, err)
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ func makeJSONResponse(err error) *C.char {
|
|||||||
errString = &errStr
|
errString = &errStr
|
||||||
}
|
}
|
||||||
|
|
||||||
out := JSONResponse{
|
out := jsonResponse{
|
||||||
Error: errString,
|
Error: errString,
|
||||||
}
|
}
|
||||||
outBytes, _ := json.Marshal(out)
|
outBytes, _ := json.Marshal(out)
|
||||||
|
|||||||
31
mobile/README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Mobile
|
||||||
|
|
||||||
|
Package mobile implements [gomobile](https://github.com/golang/mobile) bindings for go-waku.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
For properly using this package, please refer to Makefile in the root of `go-waku` directory.
|
||||||
|
|
||||||
|
To manually build library, run following commands:
|
||||||
|
|
||||||
|
### iOS
|
||||||
|
|
||||||
|
```
|
||||||
|
gomobile init
|
||||||
|
gomobile bind -v -target=ios -ldflags="-s -w" github.com/status-im/go-waku/mobile
|
||||||
|
```
|
||||||
|
This will produce `gowaku.framework` file in the current directory, which can be used in iOS project.
|
||||||
|
|
||||||
|
### Android
|
||||||
|
|
||||||
|
```
|
||||||
|
export ANDROID_NDK_HOME=/path/to/android/ndk
|
||||||
|
export ANDROID_HOME=/path/to/android/sdk/
|
||||||
|
gomobile init
|
||||||
|
gomobile bind -v -target=android -ldflags="-s -w" github.com/status-im/go-waku/mobile
|
||||||
|
```
|
||||||
|
This will generate `gowaku.aar` file in the current dir.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
See [https://github.com/golang/go/wiki/Mobile](https://github.com/golang/go/wiki/Mobile) for more information on `gomobile` usage.
|
||||||
404
mobile/api.go
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
package gowaku
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
p2pproto "github.com/libp2p/go-libp2p-core/protocol"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/node"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wakuNode *node.WakuNode
|
||||||
|
|
||||||
|
var errWakuNodeNotReady = errors.New("go-waku not initialized")
|
||||||
|
|
||||||
|
func randomHex(n int) (string, error) {
|
||||||
|
bytes := make([]byte, n)
|
||||||
|
if _, err := rand.Read(bytes); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(bytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wakuConfig struct {
|
||||||
|
Host *string `json:"host,omitempty"`
|
||||||
|
Port *int `json:"port,omitempty"`
|
||||||
|
AdvertiseAddress *string `json:"advertiseAddr,omitempty"`
|
||||||
|
NodeKey *string `json:"nodeKey,omitempty"`
|
||||||
|
KeepAliveInterval *int `json:"keepAliveInterval,omitempty"`
|
||||||
|
EnableRelay *bool `json:"relay"`
|
||||||
|
MinPeersToPublish *int `json:"minPeersToPublish"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultHost = "0.0.0.0"
|
||||||
|
var defaultPort = 60000
|
||||||
|
var defaultKeepAliveInterval = 20
|
||||||
|
var defaultEnableRelay = true
|
||||||
|
var defaultMinPeersToPublish = 0
|
||||||
|
|
||||||
|
func getConfig(configJSON string) (wakuConfig, error) {
|
||||||
|
var config wakuConfig
|
||||||
|
if configJSON != "" {
|
||||||
|
err := json.Unmarshal([]byte(configJSON), &config)
|
||||||
|
if err != nil {
|
||||||
|
return wakuConfig{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Host == nil {
|
||||||
|
config.Host = &defaultHost
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.EnableRelay == nil {
|
||||||
|
config.EnableRelay = &defaultEnableRelay
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Host == nil {
|
||||||
|
config.Host = &defaultHost
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Port == nil {
|
||||||
|
config.Port = &defaultPort
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.KeepAliveInterval == nil {
|
||||||
|
config.KeepAliveInterval = &defaultKeepAliveInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MinPeersToPublish == nil {
|
||||||
|
config.MinPeersToPublish = &defaultMinPeersToPublish
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNode(configJSON string) string {
|
||||||
|
if wakuNode != nil {
|
||||||
|
return makeJSONResponse(errors.New("go-waku already initialized. stop it first"))
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := getConfig(configJSON)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", *config.Host, *config.Port))
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var prvKey *ecdsa.PrivateKey
|
||||||
|
if config.NodeKey != nil {
|
||||||
|
prvKey, err = crypto.HexToECDSA(*config.NodeKey)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
key, err := randomHex(32)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
prvKey, err = crypto.HexToECDSA(key)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := []node.WakuNodeOption{
|
||||||
|
node.WithPrivateKey(prvKey),
|
||||||
|
node.WithHostAddress(hostAddr),
|
||||||
|
node.WithKeepAlive(time.Duration(*config.KeepAliveInterval) * time.Second),
|
||||||
|
}
|
||||||
|
|
||||||
|
if *config.EnableRelay {
|
||||||
|
opts = append(opts, node.WithWakuRelayAndMinPeers(*config.MinPeersToPublish))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
w, err := node.New(ctx, opts...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wakuNode = w
|
||||||
|
|
||||||
|
return makeJSONResponse(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start() string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := wakuNode.Start(); err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeJSONResponse(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Stop() string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
wakuNode.Stop()
|
||||||
|
wakuNode = nil
|
||||||
|
|
||||||
|
return makeJSONResponse(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PeerID() string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(wakuNode.ID(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListenAddresses() string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
var addresses []string
|
||||||
|
for _, addr := range wakuNode.ListenAddresses() {
|
||||||
|
addresses = append(addresses, addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(addresses, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddPeer(address string, protocolID string) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
ma, err := multiaddr.NewMultiaddr(address)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
peerID, err := wakuNode.AddPeer(ma, p2pproto.ID(protocolID))
|
||||||
|
return prepareJSONResponse(peerID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Connect(address string, ms int) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
|
||||||
|
if ms > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
} else {
|
||||||
|
ctx = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := wakuNode.DialPeer(ctx, address)
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConnectPeerID(peerID string, ms int) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
|
||||||
|
pID, err := peer.Decode(peerID)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ms > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
} else {
|
||||||
|
ctx = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = wakuNode.DialPeerByID(ctx, pID)
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Disconnect(peerID string) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
pID, err := peer.Decode(peerID)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = wakuNode.ClosePeerById(pID)
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PeerCnt() string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(wakuNode.PeerCount(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContentTopic(applicationName string, applicationVersion int, contentTopicName string, encoding string) string {
|
||||||
|
return protocol.NewContentTopic(applicationName, uint(applicationVersion), contentTopicName, encoding).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func PubsubTopic(name string, encoding string) string {
|
||||||
|
return protocol.NewPubsubTopic(name, encoding).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultPubsubTopic() string {
|
||||||
|
return protocol.DefaultPubsubTopic().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTopic(topic string) string {
|
||||||
|
if topic == "" {
|
||||||
|
return protocol.DefaultPubsubTopic().String()
|
||||||
|
}
|
||||||
|
return topic
|
||||||
|
}
|
||||||
|
|
||||||
|
type subscriptionMsg struct {
|
||||||
|
MessageID string `json:"messageID"`
|
||||||
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
|
Message *pb.WakuMessage `json:"wakuMessage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSubscriptionMessage(msg *protocol.Envelope) *subscriptionMsg {
|
||||||
|
return &subscriptionMsg{
|
||||||
|
MessageID: hexutil.Encode(msg.Hash()),
|
||||||
|
PubsubTopic: msg.PubsubTopic(),
|
||||||
|
Message: msg.Message(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Peers() string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
peers, err := wakuNode.Peers()
|
||||||
|
return prepareJSONResponse(peers, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalPubkey(pub []byte) (ecdsa.PublicKey, error) {
|
||||||
|
x, y := elliptic.Unmarshal(secp256k1.S256(), pub)
|
||||||
|
if x == nil {
|
||||||
|
return ecdsa.PublicKey{}, errors.New("invalid public key")
|
||||||
|
}
|
||||||
|
return ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeSymmetric(messageJSON string, symmetricKey string) string {
|
||||||
|
var msg pb.WakuMessage
|
||||||
|
err := json.Unmarshal([]byte(messageJSON), &msg)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Version == 0 {
|
||||||
|
return prepareJSONResponse(msg.Payload, nil)
|
||||||
|
} else if msg.Version > 1 {
|
||||||
|
return makeJSONResponse(errors.New("unsupported wakumessage version"))
|
||||||
|
}
|
||||||
|
|
||||||
|
keyInfo := &node.KeyInfo{
|
||||||
|
Kind: node.Symmetric,
|
||||||
|
}
|
||||||
|
|
||||||
|
keyInfo.SymKey, err = hexutil.Decode(symmetricKey)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := node.DecodePayload(&msg, keyInfo)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := struct {
|
||||||
|
PubKey string `json:"pubkey"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
Padding []byte `json:"padding"`
|
||||||
|
}{
|
||||||
|
PubKey: hexutil.Encode(crypto.FromECDSAPub(payload.PubKey)),
|
||||||
|
Signature: hexutil.Encode(payload.Signature),
|
||||||
|
Data: payload.Data,
|
||||||
|
Padding: payload.Padding,
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(response, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeAsymmetric(messageJSON string, privateKey string) string {
|
||||||
|
var msg pb.WakuMessage
|
||||||
|
err := json.Unmarshal([]byte(messageJSON), &msg)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Version == 0 {
|
||||||
|
return prepareJSONResponse(msg.Payload, nil)
|
||||||
|
} else if msg.Version > 1 {
|
||||||
|
return makeJSONResponse(errors.New("unsupported wakumessage version"))
|
||||||
|
}
|
||||||
|
|
||||||
|
keyInfo := &node.KeyInfo{
|
||||||
|
Kind: node.Asymmetric,
|
||||||
|
}
|
||||||
|
|
||||||
|
keyBytes, err := hexutil.Decode(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyInfo.PrivKey, err = crypto.ToECDSA(keyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := node.DecodePayload(&msg, keyInfo)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := struct {
|
||||||
|
PubKey string `json:"pubkey"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
Padding []byte `json:"padding"`
|
||||||
|
}{
|
||||||
|
PubKey: hexutil.Encode(crypto.FromECDSAPub(payload.PubKey)),
|
||||||
|
Signature: hexutil.Encode(payload.Signature),
|
||||||
|
Data: payload.Data,
|
||||||
|
Padding: payload.Padding,
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(response, err)
|
||||||
|
}
|
||||||
74
mobile/api_lightpush.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package gowaku
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol/lightpush"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lightpushPublish(msg pb.WakuMessage, pubsubTopic string, peerID string, ms int) (string, error) {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return "", errWakuNodeNotReady
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
|
||||||
|
if ms > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
} else {
|
||||||
|
ctx = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
var lpOptions []lightpush.LightPushOption
|
||||||
|
if peerID != "" {
|
||||||
|
p, err := peer.Decode(peerID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
lpOptions = append(lpOptions, lightpush.WithPeer(p))
|
||||||
|
} else {
|
||||||
|
lpOptions = append(lpOptions, lightpush.WithAutomaticPeerSelection(wakuNode.Host()))
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := wakuNode.Lightpush().PublishToTopic(ctx, &msg, pubsubTopic, lpOptions...)
|
||||||
|
return hexutil.Encode(hash), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func LightpushPublish(messageJSON string, topic string, peerID string, ms int) string {
|
||||||
|
msg, err := wakuMessage(messageJSON)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := lightpushPublish(msg, getTopic(topic), peerID, ms)
|
||||||
|
return prepareJSONResponse(hash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LightpushPublishEncodeAsymmetric(messageJSON string, topic string, peerID string, publicKey string, optionalSigningKey string, ms int) string {
|
||||||
|
msg, err := wakuMessageAsymmetricEncoding(messageJSON, publicKey, optionalSigningKey)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := lightpushPublish(msg, getTopic(topic), peerID, ms)
|
||||||
|
|
||||||
|
return prepareJSONResponse(hash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LightpushPublishEncodeSymmetric(messageJSON string, topic string, peerID string, symmetricKey string, optionalSigningKey string, ms int) string {
|
||||||
|
msg, err := wakuMessageSymmetricEncoding(messageJSON, symmetricKey, optionalSigningKey)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := lightpushPublish(msg, getTopic(topic), peerID, ms)
|
||||||
|
|
||||||
|
return prepareJSONResponse(hash, err)
|
||||||
|
}
|
||||||
138
mobile/api_relay.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package gowaku
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol/relay"
|
||||||
|
)
|
||||||
|
|
||||||
|
var subscriptions map[string]*relay.Subscription = make(map[string]*relay.Subscription)
|
||||||
|
var mutex sync.Mutex
|
||||||
|
|
||||||
|
func RelayEnoughPeers(topic string) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
topicToCheck := protocol.DefaultPubsubTopic().String()
|
||||||
|
if topic != "" {
|
||||||
|
topicToCheck = topic
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(wakuNode.Relay().EnoughPeersToPublishToTopic(topicToCheck), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func relayPublish(msg pb.WakuMessage, pubsubTopic string, ms int) (string, error) {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return "", errWakuNodeNotReady
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
|
||||||
|
if ms > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
} else {
|
||||||
|
ctx = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := wakuNode.Relay().PublishToTopic(ctx, &msg, pubsubTopic)
|
||||||
|
return hexutil.Encode(hash), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func RelayPublish(messageJSON string, topic string, ms int) string {
|
||||||
|
msg, err := wakuMessage(messageJSON)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := relayPublish(msg, getTopic(topic), int(ms))
|
||||||
|
return prepareJSONResponse(hash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RelayPublishEncodeAsymmetric(messageJSON string, topic string, publicKey string, optionalSigningKey string, ms int) string {
|
||||||
|
msg, err := wakuMessageAsymmetricEncoding(messageJSON, publicKey, optionalSigningKey)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := relayPublish(msg, getTopic(topic), int(ms))
|
||||||
|
|
||||||
|
return prepareJSONResponse(hash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RelayPublishEncodeSymmetric(messageJSON string, topic string, symmetricKey string, optionalSigningKey string, ms int) string {
|
||||||
|
msg, err := wakuMessageSymmetricEncoding(messageJSON, symmetricKey, optionalSigningKey)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := relayPublish(msg, getTopic(topic), int(ms))
|
||||||
|
|
||||||
|
return prepareJSONResponse(hash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RelaySubscribe(topic string) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
topicToSubscribe := getTopic(topic)
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
subscription, ok := subscriptions[topicToSubscribe]
|
||||||
|
if ok {
|
||||||
|
return makeJSONResponse(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscription, err := wakuNode.Relay().SubscribeToTopic(context.Background(), topicToSubscribe)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions[topicToSubscribe] = subscription
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for envelope := range subscription.C {
|
||||||
|
send("message", toSubscriptionMessage(envelope))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return makeJSONResponse(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RelayUnsubscribe(topic string) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
topicToUnsubscribe := getTopic(topic)
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
subscription, ok := subscriptions[topicToUnsubscribe]
|
||||||
|
if ok {
|
||||||
|
return makeJSONResponse(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscription.Unsubscribe()
|
||||||
|
|
||||||
|
delete(subscriptions, topicToUnsubscribe)
|
||||||
|
|
||||||
|
err := wakuNode.Relay().Unsubscribe(context.Background(), topicToUnsubscribe)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeJSONResponse(nil)
|
||||||
|
}
|
||||||
99
mobile/api_store.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package gowaku
|
||||||
|
|
||||||
|
import (
|
||||||
|
"C"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
||||||
|
"github.com/status-im/go-waku/waku/v2/protocol/store"
|
||||||
|
)
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type storePagingOptions struct {
|
||||||
|
PageSize uint64 `json:"pageSize,omitempty"`
|
||||||
|
Cursor *pb.Index `json:"cursor,omitempty"`
|
||||||
|
Forward bool `json:"forward,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type storeMessagesArgs struct {
|
||||||
|
Topic string `json:"pubsubTopic,omitempty"`
|
||||||
|
ContentFilters []string `json:"contentFilters,omitempty"`
|
||||||
|
StartTime int64 `json:"startTime,omitempty"`
|
||||||
|
EndTime int64 `json:"endTime,omitempty"`
|
||||||
|
PagingOptions storePagingOptions `json:"pagingOptions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type storeMessagesReply struct {
|
||||||
|
Messages []*pb.WakuMessage `json:"messages,omitempty"`
|
||||||
|
PagingInfo storePagingOptions `json:"pagingInfo,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreQuery(queryJSON string, peerID string, ms int) string {
|
||||||
|
if wakuNode == nil {
|
||||||
|
return makeJSONResponse(errWakuNodeNotReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
var args storeMessagesArgs
|
||||||
|
err := json.Unmarshal([]byte(queryJSON), &args)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
options := []store.HistoryRequestOption{
|
||||||
|
store.WithAutomaticRequestId(),
|
||||||
|
store.WithPaging(args.PagingOptions.Forward, args.PagingOptions.PageSize),
|
||||||
|
store.WithCursor(args.PagingOptions.Cursor),
|
||||||
|
}
|
||||||
|
|
||||||
|
if peerID != "" {
|
||||||
|
p, err := peer.Decode(peerID)
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
options = append(options, store.WithPeer(p))
|
||||||
|
} else {
|
||||||
|
options = append(options, store.WithAutomaticPeerSelection())
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := storeMessagesReply{}
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
|
||||||
|
if ms > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
} else {
|
||||||
|
ctx = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := wakuNode.Store().Query(
|
||||||
|
ctx,
|
||||||
|
store.Query{
|
||||||
|
Topic: args.Topic,
|
||||||
|
ContentTopics: args.ContentFilters,
|
||||||
|
StartTime: args.StartTime,
|
||||||
|
EndTime: args.EndTime,
|
||||||
|
},
|
||||||
|
options...,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
reply.Error = err.Error()
|
||||||
|
return prepareJSONResponse(reply, nil)
|
||||||
|
}
|
||||||
|
reply.Messages = res.Messages
|
||||||
|
reply.PagingInfo = storePagingOptions{
|
||||||
|
PageSize: args.PagingOptions.PageSize,
|
||||||
|
Cursor: res.Cursor(),
|
||||||
|
Forward: args.PagingOptions.Forward,
|
||||||
|
}
|
||||||
|
|
||||||
|
return prepareJSONResponse(reply, nil)
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package main
|
package gowaku
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -1,6 +1,7 @@
|
|||||||
|
//go:build darwin && cgo
|
||||||
// +build darwin,cgo
|
// +build darwin,cgo
|
||||||
|
|
||||||
package main
|
package gowaku
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo CFLAGS: -x objective-c
|
#cgo CFLAGS: -x objective-c
|
||||||
41
mobile/response.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package gowaku
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type jsonResponse struct {
|
||||||
|
Error *string `json:"error,omitempty"`
|
||||||
|
Result interface{} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareJSONResponse(result interface{}, err error) string {
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errStr := err.Error()
|
||||||
|
errResponse := jsonResponse{
|
||||||
|
Error: &errStr,
|
||||||
|
}
|
||||||
|
response, _ := json.Marshal(&errResponse)
|
||||||
|
return string(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(jsonResponse{Result: result})
|
||||||
|
if err != nil {
|
||||||
|
return prepareJSONResponse(nil, err)
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeJSONResponse(err error) string {
|
||||||
|
var errString *string = nil
|
||||||
|
if err != nil {
|
||||||
|
errStr := err.Error()
|
||||||
|
errString = &errStr
|
||||||
|
}
|
||||||
|
|
||||||
|
out := jsonResponse{
|
||||||
|
Error: errString,
|
||||||
|
}
|
||||||
|
outBytes, _ := json.Marshal(out)
|
||||||
|
|
||||||
|
return string(outBytes)
|
||||||
|
}
|
||||||
@ -1,13 +1,14 @@
|
|||||||
package main
|
package gowaku
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
extern bool StatusServiceSignalEvent(const char *jsonEvent);
|
extern bool StatusServiceSignalEvent(const char *jsonEvent);
|
||||||
extern void SetEventCallback(void *cb);
|
extern void SetEventCallback(void *cb);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -27,15 +28,15 @@ type MobileSignalHandler func([]byte)
|
|||||||
// storing the current mobile signal handler here
|
// storing the current mobile signal handler here
|
||||||
var mobileSignalHandler MobileSignalHandler
|
var mobileSignalHandler MobileSignalHandler
|
||||||
|
|
||||||
// SignalEnvelope is a general signal sent upward from node to app
|
// signalEnvelope is a general signal sent upward from node to app
|
||||||
type SignalEnvelope struct {
|
type signalEnvelope struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Event interface{} `json:"event"`
|
Event interface{} `json:"event"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEnvelope creates new envlope of given type and event payload.
|
// NewEnvelope creates new envlope of given type and event payload.
|
||||||
func NewEnvelope(signalType string, event interface{}) *SignalEnvelope {
|
func newEnvelope(signalType string, event interface{}) *signalEnvelope {
|
||||||
return &SignalEnvelope{
|
return &signalEnvelope{
|
||||||
Type: signalType,
|
Type: signalType,
|
||||||
Event: event,
|
Event: event,
|
||||||
}
|
}
|
||||||
@ -43,7 +44,8 @@ func NewEnvelope(signalType string, event interface{}) *SignalEnvelope {
|
|||||||
|
|
||||||
// send sends application signal (in JSON) upwards to application (via default notification handler)
|
// send sends application signal (in JSON) upwards to application (via default notification handler)
|
||||||
func send(signalType string, event interface{}) {
|
func send(signalType string, event interface{}) {
|
||||||
signal := NewEnvelope(signalType, event)
|
|
||||||
|
signal := newEnvelope(signalType, event)
|
||||||
data, err := json.Marshal(&signal)
|
data, err := json.Marshal(&signal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("marshal signal error", err)
|
fmt.Println("marshal signal error", err)
|
||||||
@ -71,6 +73,6 @@ func SetMobileSignalHandler(handler SignalHandler) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setEventCallback(cb unsafe.Pointer) {
|
func SetEventCallback(cb unsafe.Pointer) {
|
||||||
C.SetEventCallback(cb)
|
C.SetEventCallback(cb)
|
||||||
}
|
}
|
||||||