Skip to content

Commit

Permalink
Merge pull request #10 from ssbc/apk_in-order-delivery
Browse files Browse the repository at this point in the history
android: add additional frontend callbacks for received log entries
  • Loading branch information
tschudin authored Dec 12, 2023
2 parents d3d7978 + acd110b commit 59c6698
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 13 deletions.
85 changes: 83 additions & 2 deletions android/tinySSB/app/src/main/assets/web/tremola.js
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,89 @@ function b2f_local_peer(type, identifier, displayname, status) {
refresh_connection_entry(identifier)
}

/**
* This function is called, when the backend received a new log entry and successfully completed the corresponding sidechain.
* The backend assures, that the log entries are sent to the frontend in the exact same sequential order as in the append-only log.
*
* @param {Object} e Object containing all information of the log_entry.
* @param {Object} e.hdr Contains basic information about the log entry.
* @param {number} e.hdr.tst Timestamp at which the message was created. (Number of milliseconds elapsed since midnight at the beginning of January 1970 00:00 UTC)
* @param {string} e.hdr.ref The message ID of this log entry.
* @param {string} e.hdr.fid The public key of the author encoded in base64.
* @param {[]} e.public The payload of the message. The first entry is a String that represents the application to which the message belongs. All additional entries are application-specific parameters.
*
*/
function b2f_new_in_order_event(e) {

console.log("b2f inorder event:", JSON.stringify(e.public))

if (!(e.header.fid in tremola.contacts)) {
var a = id2b32(e.header.fid);
tremola.contacts[e.header.fid] = {
"alias": a, "initial": a.substring(0, 1).toUpperCase(),
"color": colors[Math.floor(colors.length * Math.random())],
"iam": "", "forgotten": false
}
load_contact_list()
}

switch (e.public[0]) {
case "KAN":
console.log("New kanban event")
kanban_new_event(e)
break
default:
return
}
persist();
must_redraw = true;
}

/**
* This function is invoked whenever the backend receives a new log entry, regardless of whether the associated sidechain is fully loaded or not.
*
* @param {Object} e Object containing all information of the log_entry.
* @param {Object} e.hdr Contains basic information about the log entry.
* @param {number} e.hdr.tst Timestamp at which the message was created. (Number of milliseconds elapsed since midnight at the beginning of January 1970 00:00 UTC)
* @param {string} e.hdr.ref The message ID of this log entry.
* @param {string} e.hdr.fid The public key of the author encoded in base64.
* @param {[]} e.public The payload of the logentry, without the content of the sidechain
*
*/
function b2f_new_incomplete_event(e) {

if (!(e.header.fid in tremola.contacts)) {
var a = id2b32(e.header.fid);
tremola.contacts[e.header.fid] = {
"alias": a, "initial": a.substring(0, 1).toUpperCase(),
"color": colors[Math.floor(colors.length * Math.random())],
"iam": "", "forgotten": false
}
load_contact_list()
}

switch (e.public[0]) {
default:
return
}
persist();
must_redraw = true;


}

/**
* This function is called, when the backend received a new log entry and successfully completed the corresponding sidechain.
* This callback does not ensure any specific order; the log entries are forwarded in the order they are received.
*
* @param {Object} e Object containing all information of the log_entry.
* @param {Object} e.hdr Contains basic information about the log entry.
* @param {number} e.hdr.tst Timestamp at which the message was created. (Number of milliseconds elapsed since midnight at the beginning of January 1970 00:00 UTC)
* @param {string} e.hdr.ref The message ID of this log entry.
* @param {string} e.hdr.fid The public key of the author encoded in base64.
* @param {[]} e.public The payload of the message. The first entry is a String that represents the application to which the message belongs. All additional entries are application-specific parameters.
*
*/
function b2f_new_event(e) { // incoming SSB log event: we get map with three entries
// console.log('hdr', JSON.stringify(e.header))
console.log('pub', JSON.stringify(e.public))
Expand Down Expand Up @@ -1081,8 +1164,6 @@ function b2f_new_event(e) { // incoming SSB log event: we get map with three ent
// if (curr_scenario == "chats") // the updated conversation could bubble up
load_chat_list();
} else if (e.public[0] == "KAN") { // Kanban board event
console.log("New kanban event")
kanban_new_event(e)
} else if (e.public[0] == "IAM") {
var contact = tremola.contacts[e.header.fid]
var old_iam = contact.iam
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package nz.scuttlebutt.tremolavossbol
import android.Manifest
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.util.Base64
Expand Down Expand Up @@ -30,6 +31,7 @@ import org.json.JSONArray
class WebAppInterface(val act: MainActivity, val webView: WebView) {

var frontend_ready = false
val frontend_frontier = act.getSharedPreferences("frontend_frontier", Context.MODE_PRIVATE)

@JavascriptInterface
fun onFrontendRequest(s: String) {
Expand Down Expand Up @@ -63,7 +65,7 @@ class WebAppInterface(val act: MainActivity, val webView: WebView) {
val mid = r.get_mid(i)
if (payload == null || mid == null) break
Log.d("restream", "${i}, ${payload.size} Bytes")
sendToFrontend(fid, i, mid, payload)
sendTinyEventToFrontend(fid, i, mid, payload)
i++
}
}
Expand Down Expand Up @@ -353,28 +355,48 @@ class WebAppInterface(val act: MainActivity, val webView: WebView) {
eval(cmd)
}

fun sendIncompleteEntryToFrontend(fid: ByteArray, seq: Int, mid:ByteArray, body: ByteArray) {
val e = toFrontendObject(fid, seq, mid, body)
if (e != null)
eval("b2f_new_incomplete_event($e)")

}

fun sendTinyEventToFrontend(fid: ByteArray, seq: Int, mid:ByteArray, body: ByteArray) {
Log.d("wai","sendTinyEvent ${body.toHex()}")
sendToFrontend(fid, seq, mid, body)
var e = toFrontendObject(fid, seq, mid, body)
if (e != null)
eval("b2f_new_event($e)")

// in-order api
val replica = act.tinyRepo.fid2replica(fid)

if (frontend_frontier.getInt(fid.toHex(), 1) == seq && replica != null) {
for (i in seq .. replica.state.max_seq ) {
val content = replica.read_content(i)
val message_id= replica.get_mid(seq)
if(content == null || message_id == null || !replica.isSidechainComplete(i))
break
e = toFrontendObject(fid, i, message_id, content)
if (e != null)
eval("b2f_new_in_order_event($e)")
frontend_frontier.edit().putInt(fid.toHex(), i + 1).apply()
}
}
}

fun sendToFrontend(fid: ByteArray, seq: Int, mid: ByteArray, payload: ByteArray) {
Log.d("wai", "sendToFrontend seq=${seq} ${payload.toHex()}")
fun toFrontendObject(fid: ByteArray, seq: Int, mid: ByteArray, payload: ByteArray): String? {
val bodyList = Bipf.decode(payload)
if (bodyList == null || bodyList.typ != BIPF_LIST) {
Log.d("sendToFrontend", "decoded payload == null")
return
Log.d("toFrontendObject", "decoded payload == null")
return null
}
val param = Bipf.bipf_list2JSON(bodyList)
var hdr = JSONObject()
hdr.put("fid", "@" + fid.toBase64() + ".ed25519")
hdr.put("ref", mid.toBase64())
hdr.put("seq", seq)
var cmd = "b2f_new_event({header:${hdr.toString()},"
cmd += "public:${param.toString()}"
cmd += "});"
Log.d("CMD", cmd)
eval(cmd)
return "{header:${hdr.toString()}, public:${param.toString()}}"
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ class Replica(val context: MainActivity, val datapath: File, val fid: ByteArray)
val fct = { buf: ByteArray, fid: ByteArray?, _: String? -> context.tinyNode.incoming_pkt(buf,fid!!) }
context.tinyDemux.arm_dmx(new_dmx, fct, fid)

val (_, sz) = Bipf.varint_decode(pkt, DMX_LEN + 1, DMX_LEN + 4)
val content = pkt.sliceArray(8 + sz until 36)

context.wai.sendIncompleteEntryToFrontend(fid, seq, (nam + pkt).sha256().sliceArray(0 until HASH_LEN), content)

if(sendToFront)
context.wai.sendTinyEventToFrontend(fid, seq, (nam + pkt).sha256().sliceArray(0 until HASH_LEN), read_content(seq)!!)
return true
Expand Down Expand Up @@ -446,4 +451,9 @@ class Replica(val context: MainActivity, val datapath: File, val fid: ByteArray)
Log.d("replica", "write success, len: ${log_entry.size}")
return seq
}

fun isSidechainComplete(seq: Int): Boolean {
return !state.pend_sc.containsKey(seq)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ class Repo(val context: MainActivity) {
frontier.delete()
frontier.createNewFile()
frontier.appendBytes(new_state.toWire())
context.wai.frontend_frontier.edit().putInt(f.name, new_state.max_pos + 1).apply()

Files.move(new_log.toPath(), File(feed_dir, "log.bin").toPath(), StandardCopyOption.ATOMIC_MOVE)
File(feed_dir, "log").delete()
Expand Down

0 comments on commit 59c6698

Please sign in to comment.