Commit 44c623d8 authored by Max's avatar Max

Background/Ui/Storage/Inpage: handle batch operations

parent 10e00243
......@@ -32,9 +32,10 @@ open Storage_types
open Local
open Chrome
type manager_op = notif_manager_info_js t manager_operation_js t
type manager_op = notif_manager_info_js t manager_operation_js
let pending_approved : (string * siteMetadata t) list ref = ref []
let pending_op : (string * manager_op) list ref = ref []
let pending_op : (string * manager_op t) list ref = ref []
let pending_batch : (string * manager_op t js_array t) list ref = ref []
let port_table : (string, Runtime_utils.port t) Hashtbl.t = Hashtbl.create 100
......@@ -235,6 +236,7 @@ let check_list l = List.fold_left (fun acc x -> match acc, x with
| Error e, Ok () | Ok (), Error e -> Error e
| Error e , Error e2 -> Error (e @ e2)
| Ok (), Ok () -> Ok ()) (Ok ()) l
let check_array f a = check_list @@ List.map f @@ Array.to_list @@ to_array a
let check_optdef f x = match Js.Optdef.to_option x with
| None -> Ok ()
| Some x -> f x
......@@ -300,7 +302,7 @@ let check_kind ?kind s =
let unit_optdef x = match Js.Optdef.to_option x with
| None -> undefined
| Some _ -> def ()
let check_manager_operation ?kind (o: manager_op) =
let check_manager_operation ?kind (o: manager_op t) =
check_list [
check_notif_manager_info o##.info;
check_kind ?kind o##.kind;
......@@ -360,7 +362,7 @@ let dispatch (port : Runtime_utils.port t) (msg : message t) =
def_case
(msg##.req##.data)
(fun () -> send port req @@ string "EMPTY DATA")
(fun (data : manager_op) ->
(fun (data : manager_op t) ->
match List.assoc_opt id !pending_op with
| Some _ -> send port req @@ string "ID already used"
| None ->
......@@ -374,6 +376,26 @@ let dispatch (port : Runtime_utils.port t) (msg : message t) =
Hashtbl.add port_table id port;
wrap_handler port req f
)
| "batch" ->
def_case
(msg##.req##.data)
(fun () -> send port req @@ string "EMPTY DATA")
(fun (data : manager_op t js_array t) ->
match List.assoc_opt id !pending_batch with
| Some _ -> send port req @@ string "ID already used"
| None ->
let f () =
match check_array check_manager_operation data with
| Ok () ->
pending_batch := (id, data) :: !pending_batch;
Notification.show_popup_action ~id port (fun () -> ()) meth
| Error errs ->
send_error port req errs
in
Hashtbl.add port_table id port;
wrap_handler port req f
)
| _ -> failwith ("[Background|dispatch] Don't know what to do with : " ^ meth)
else
Js.raise_js_error @@
......@@ -395,13 +417,20 @@ let dispatch_popup port (msg : message t) =
let err = new%js Js.error_constr (string "Can't find origin request") in
send port req err
end
| "get_trdata" | "get_oridata" | "get_dlgdata" ->
| "get_opdata" ->
begin match List.assoc_opt id !pending_op with
| Some opdata -> send port req opdata
| _ ->
let err = new%js Js.error_constr (string "Can't find origin request") in
send port req err
end
| "get_batchdata" ->
begin match List.assoc_opt id !pending_batch with
| Some opdata -> send port req opdata
| _ ->
let err = new%js Js.error_constr (string "Can't find origin request") in
send port req err
end
| "state_changed" -> state_changed ()
| "callback_error_notif" ->
begin match to_def_option req##.data with
......@@ -416,8 +445,8 @@ let dispatch_popup port (msg : message t) =
pending_approved := List.remove_assoc id !pending_approved)
data
notif
| OpNot n as notif ->
if n.not_id = to_string req##.id then
| notif ->
if Mhelpers.notif_id_of_not notif = to_string req##.id then
callback_handler ~callback:(fun _ ->
let obj =
Metal_message.mk_answer
......@@ -452,8 +481,8 @@ let dispatch_popup port (msg : message t) =
remove_window n.not_approv_wid)
data
notif
| OpNot n as notif ->
if n.not_id = to_string req##.id then
| notif ->
if Mhelpers.notif_id_of_not notif = to_string req##.id then
callback_handler
~callback:(fun _ ->
let obj = Metal_message.mk_answer ~src:"background" req##.id data in
......@@ -463,7 +492,7 @@ let dispatch_popup port (msg : message t) =
remove_port port ;
port##postMessage (obj)
end ;
remove_window n.not_wid)
remove_window @@ Mhelpers.notif_wid_of_not notif)
data
notif)
notifs)
......
......@@ -45,18 +45,15 @@ let mk_message ?data ~name ~src id =
let mk_metadata_req src rid =
mk_message ~name:"get_metadata" ~src rid
let mk_tr_req src rid =
mk_message ~name:"get_trdata" ~src rid
let mk_ori_req src rid =
mk_message ~name:"get_oridata" ~src rid
let mk_dlg_req src rid =
mk_message ~name:"get_dlgdata" ~src rid
let mk_op_req src rid =
mk_message ~name:"get_opdata" ~src rid
let mk_state_changed_notif () =
mk_message ~name:"state_changed" ~src:"popup" "-1"
let mk_batch_req src rid =
mk_message ~name:"get_batchdata" ~src rid
let mk_answer ~src id res =
let res =
let obj : response t = Unsafe.obj [||] in
......
......@@ -121,14 +121,20 @@ type notif_manager_info = {
not_mi_msg : string option
}
type notif_op = {
type 'a notif_generic = {
not_origin : string;
not_tsp : string;
not_id : string;
not_wid : int;
not_op : (notif_manager_info, string option manager_details) manager_operation;
not_op : 'a
}
type notif_op =
(notif_manager_info, string option manager_details) manager_operation notif_generic
type notif_batch =
(notif_manager_info, string option manager_details) manager_operation list notif_generic
type notif_app = {
not_approv_id : string ;
not_approv_wid : int ;
......@@ -140,6 +146,7 @@ type notif_app = {
type notif_kind =
| OpNot of notif_op
| BatchNot of notif_batch
| ApprovNot of notif_app
type vault =
......
......@@ -105,10 +105,17 @@ let flatten_block_operations f bos =
let notif_id_of_not = function
| OpNot n -> n.not_id
| BatchNot n -> n.not_id
| ApprovNot n -> n.not_approv_id
let notif_wid_of_not = function
| OpNot n -> n.not_wid
| BatchNot n -> n.not_wid
| ApprovNot n -> n.not_approv_wid
let notif_tsp_of_not = function
| OpNot n -> n.not_tsp
| BatchNot n -> n.not_tsp
| ApprovNot n -> n.not_approv_tsp
let parse_script = function
......@@ -122,6 +129,11 @@ let make_notif_op ~id ~wid ~tsp ~origin not_op =
not_origin = origin; not_tsp = tsp;
not_id = id; not_wid = wid ; not_op }
let make_notif_batch ~id ~wid ~tsp ~origin not_op =
BatchNot {
not_origin = origin; not_tsp = tsp;
not_id = id; not_wid = wid ; not_op }
let make_notif_approv ~id ~wid ~tsp ~icon ~name ~url =
ApprovNot {
not_approv_id = id ;
......
......@@ -277,7 +277,6 @@ module Local = struct
method msg : js_string t optdef prop
end
class type notif_js = object
method kind : js_string t prop
method id : js_string t prop
......@@ -287,6 +286,8 @@ module Local = struct
method msg : js_string t optdef prop
(* Operation *)
method op : notif_manager_info_js t manager_operation_js t prop
(* Batch *)
method batch : notif_manager_info_js t manager_operation_js t js_array t prop
(* Approv *)
method icon : js_string t optdef prop
method name : js_string t prop
......
......@@ -182,6 +182,15 @@ module Of_js = struct
not_op = manager_operation notif_manager_info o##.op
}
let notif_batch (o: notif_js t) =
BatchNot {
not_origin = to_string o##.origin;
not_tsp = to_string o##.tsp;
not_id = to_string o##.id;
not_wid = o##.wid;
not_op = to_list (manager_operation notif_manager_info) o##.batch
}
let notif_app (o: notif_js t) =
let not_approv_id = to_string o##.id in
let not_approv_wid = o##.wid in
......@@ -201,7 +210,10 @@ module Of_js = struct
let notif (o: notif_js t) =
match (to_string o##.kind) with
| "approval" -> notif_app o
| _ -> notif_op o
| "operation" -> notif_op o
| "batch" -> notif_batch o
| _ -> Js_utils.log "storage_reader: type of notification not recognised";
assert false
let acc_notif (o: notif_acc t) =
let notif_acc = to_string o##.acc in
......@@ -417,6 +429,13 @@ module To_js = struct
notif##.id := string n.not_id;
notif##.wid := n.not_wid;
notif##.op := manager_operation notif_manager_info n.not_op;
| BatchNot n ->
notif##.kind := string "batch";
notif##.origin := string n.not_origin;
notif##.tsp := string n.not_tsp;
notif##.id := string n.not_id;
notif##.wid := n.not_wid;
notif##.batch := of_list (manager_operation notif_manager_info) n.not_op;
| ApprovNot n ->
notif##.kind := string "approval";
notif##.id := string n.not_approv_id;
......
......@@ -44,6 +44,8 @@ open Mhelpers
open Async
module Dune = Dune_types_min
let error_callback n code err =
let id = notif_id_of_not n in
let data = object%js
......@@ -282,6 +284,84 @@ let mk_op_page ~(confirm: ?msg:'a Js.t -> Metal_types.notif_kind -> unit)
Js.raise_js_error @@
(new%js Js.error_constr
(Js.string @@ "[Core] TODO op page"))
| BatchNot {not_op; _} ->
let ops = List.fold_left (fun acc -> function
| {mo_det = TraDetails trd; mo_info} -> {mo_det = TraDetails trd; mo_info} :: acc
| {mo_det = OriDetails ord; mo_info} -> {mo_det = OriDetails ord; mo_info} :: acc
| {mo_det = DelDetails del; mo_info} -> {mo_det = DelDetails del; mo_info} :: acc
| {mo_det = RvlDetails rvl; mo_info} -> {mo_det = RvlDetails rvl; mo_info} :: acc
| _ -> acc) [] not_op in
Mrequest.Node.forge_manager_operations ~state ops >>|? fun (op_bytes, ops) ->
let fee, gas_limit, storage_limit = Dune_utils.limits_of_operations ops in
let burn = Int64.mul 1000L @@ Z.to_int64 storage_limit in
let amount = List.fold_left (fun acc op -> match op with
| Dune.NTransaction tr -> Int64.add acc tr.Dune.node_tr_amount
| Dune.NOrigination ori -> Int64.add acc ori.Dune.node_or_balance
| _ -> acc) 0L ops in
let amount_div = make_amount_div amount fee burn in
let tr_src_div = make_tr_div (div []) in
let n, pane_ids, op_panes = List.fold_left (fun (i, ids, panes) op ->
let fee, _gas_limit, storage_limit = Dune_utils.limits_of_operations [ op ] in
let burn = Int64.mul 1000L @@ Z.to_int64 storage_limit in
let amount_div, dst_div = match op with
| Dune.NTransaction tr ->
let dst = tr.Dune.node_tr_dst in
Some (make_amount_div tr.Dune.node_tr_amount fee burn),
Some (div ~a:[ a_class [ Display.d_flex ; Flex.flex_row ;
Flex.align_items_center ; Spacing.mxa;
Spacing.mb2] ] [
blockies ~classes:["account-blockies" ; Spacing.mr3] dst;
span [ txt dst ] ])
| Dune.NOrigination ori ->
Some (make_amount_div ori.Dune.node_or_balance fee burn),
Some (span ~a:[ a_class [ Text.center; Spacing.mb2 ] ] [ txt "New contract" ])
| Dune.NDelegation del ->
Some (make_amount_div 0L fee 0L),
Some (div ~a:[ a_class [ Display.d_flex ; Flex.flex_row ;
Flex.align_items_center ; Spacing.mxa;
Spacing.mb2] ]
(match del.Dune.node_del_delegate with
| None -> [ span [ txt "Unset Delegate" ] ]
| Some delegate ->
[ blockies
~classes:["account-blockies" ; Spacing.mx3]
delegate ;
span [ txt delegate ] ]))
| _ -> None, None in
match amount_div, dst_div with
| Some amount_div, Some dst_div when List.length ids < 10 ->
i+1, (("op_" ^ (string_of_int i)), string_of_int i) :: ids,
div ~a:[ a_class [ Display.d_flex ; Flex.flex_column ] ] (
dst_div :: amount_div) :: panes
| _ -> i+1, ids, panes)
(1, [], []) ops in
let pane_ids, op_panes =
if List.length pane_ids < (n - 1) then
("last_op", "... " ^ (string_of_int n)) :: pane_ids,
div [] :: op_panes
else pane_ids, op_panes in
let pane_ids, op_panes = List.rev pane_ids, List.rev op_panes in
let detail_divs = add_gas_limit gas_limit [] in
let detail_divs = add_storage_limit storage_limit detail_divs in
let tr_info = div ~a:[ a_class [ Text.left ] ] [
ul ~a:[ a_class [ Nav.nav ; Nav.nav_tabs ;
Nav.navbar_dark ; Spacing.px1 ];
a_role [ "tablist" ] ] ([
make_tab_li ~active:true "amount" "Amount";
make_tab_li "details" "Details" ] @
List.map (fun (id, name) -> make_tab_li id name) pane_ids);
div ~a:[ a_class [ Nav.tab_content ] ] ([
make_tab_pane ~active:true "amount" amount_div;
make_tab_pane "details" (List.rev detail_divs) ] @
List.map2 (fun p (id, _)-> make_tab_pane id [ p ]) op_panes pane_ids)
] in
let cb_ok = (fun () ->
Mrequest.Node.send_manager_operations ~state
~error:(fun _c _msg -> ignore @@ deny notif) op_bytes ops
(fun (op_hash, _, _ops) -> confirm ~msg:(Js.string op_hash) notif)
) in
[ tr_src_div ; tr_info ; alert cb_ok ]
| OpNot {not_op; _} ->
match not_op.mo_det with
| TraDetails trd ->
......@@ -1042,7 +1122,7 @@ let update_full_notif refresh state n =
[ p ~a:[ a_class [ "header-info" ; Text.center ] ] [ txt "Approval Request" ] ] ;
match n with
| ApprovNot _ -> update_approve_page refresh state n
| OpNot _ ->
| _ ->
let forging_dom = make_forging_dom () in
replace_fade_id center_id [ forging_dom ] ;
update_op_page refresh state n
......
......@@ -151,7 +151,7 @@ let update_op_page refresh state notif =
let update_full_notif refresh state n = match n with
| ApprovNot _ -> update_approve_page refresh state n
| OpNot _ -> update_op_page refresh state n
| _ -> update_op_page refresh state n
let add_notif_modal notif =
let nid = Mhelpers.notif_id_of_not notif in
......@@ -165,6 +165,7 @@ let add_notif_modal notif =
| OpNot {not_op={mo_det=OriDetails _; _}; _} -> "Origination Request"
| OpNot {not_op={mo_det=ManDetails _; _}; _} -> "Manage Account Request"
| OpNot {not_op={mo_det=RvlDetails _; _}; _} -> "Reveal Request"
| BatchNot _ -> "Batch Request"
in
let nmod =
div ~a:[ a_id id_modal ;
......
......@@ -41,9 +41,7 @@ open Metal_message_types
open Metal_types
let remove_window notif =
let wid = match notif with
| OpNot n -> n.not_wid
| ApprovNot n -> n.not_approv_wid in
let wid = Mhelpers.notif_wid_of_not notif in
Windows.getAll (fun wins ->
List.iter (fun win ->
Js_types.def_case
......@@ -254,7 +252,7 @@ let update_op_page notif =
let make_op_page wid args =
let _, rid = List.find (fun (n, _v) -> n = "req_id") args in
Js_utils.log "make_op_page %s" rid ;
let msg : message Js.t = Metal_message.mk_tr_req "popup" rid in
let msg : message Js.t = Metal_message.mk_op_req "popup" rid in
let info = Runtime_utils.mk_connection_info "popup" in
let port = Runtime.connect ~info () in
port##postMessage msg ;
......@@ -294,9 +292,7 @@ let make_op_page wid args =
notif_acc = acc.pkh) ns).notifs
with Not_found -> [] in
let n_notif = List.length notifs in
if (List.exists
(function ApprovNot n -> n.not_approv_wid = wid
| OpNot n -> n.not_wid = wid)
if (List.exists (fun n -> Mhelpers.notif_wid_of_not n = wid)
notifs) then
update_op_page notif
else
......@@ -313,6 +309,68 @@ let make_op_page wid args =
(* make_error_page *) ()
)
let make_batch_page wid args =
let _, rid = List.find (fun (n, _v) -> n = "req_id") args in
Js_utils.log "make_batch_page %s" rid ;
let msg : message Js.t = Metal_message.mk_batch_req "popup" rid in
let info = Runtime_utils.mk_connection_info "popup" in
let port = Runtime.connect ~info () in
port##postMessage msg ;
Browser_utils.addListener1
(port##.onMessage)
(fun ans ->
Js_utils.js_log ans ;
if msg##.req##.id = ans##.res##.id &&
ans##.src = Js.string "background" then
begin
Storage_reader.get_account (fun (acc, _) -> match acc, Js.Optdef.to_option wid with
| Some acc, Some wid ->
let open Js_types in
let (data :
Storage_types.Local.notif_manager_info_js t
Storage_types.Local.manager_operation_js t js_array t) =
ans##.res##.result in
let tsp = Jsdate.now_tsp () in
let not_op = Storage_utils.(Of_js.(
to_list (manager_operation notif_manager_info) data)) in
let origin = match to_def_option port##.sender with
| None -> "unknown"
| Some sender -> match to_def_option sender##.url with
| None -> "unknown"
| Some origin -> to_string origin in
let notif =
Mhelpers.make_notif_batch
~id:rid
~wid
~tsp
~origin
not_op
in
Storage_reader.get_notifs (fun ns ->
let notifs =
try
(List.find (fun {notif_acc ; _} ->
notif_acc = acc.pkh) ns).notifs
with Not_found -> [] in
let n_notif = List.length notifs in
if (List.exists (fun n -> Mhelpers.notif_wid_of_not n = wid)
notifs) then
update_op_page notif
else
Storage_writer.add_notif
~callback:(fun _ ->
Browser_action.set_badge ~text:(string_of_int @@ n_notif + 1) () ;
update_op_page notif)
acc.pkh
notif)
| _, _ -> ()
) ;
end
else
(* make_error_page *) ()
)
let dispatch id args =
try
let _, typ = List.find (fun (n, _v) -> n = "type") args in
......@@ -321,6 +379,7 @@ let dispatch id args =
| "unlock" -> make_unlock_page ()
| "approve" -> make_approve_page_metadata id args
| "send" | "originate" | "delegate" -> make_op_page id args
| "batch" -> make_batch_page id args
| _ -> Js_utils.log "don't know what to do with %s" typ
with Not_found ->
Js_utils.log "No type argument"
......
......@@ -212,7 +212,7 @@ let update_badge account =
Chrome.Browser_action.set_badge
~text:(if n_notif = 0 then "" else string_of_int n_notif) ())
let notif_kind_to_row network = function
let rec notif_kind_to_row network = function
| OpNot { not_op = {mo_det = TraDetails trd; _}; _ } ->
div ~a:[ a_class [
Display.d_flex; Flex.justify_center ;
......@@ -287,6 +287,10 @@ let notif_kind_to_row network = function
| None -> []
| Some None -> [ span [ txt "unset recovery" ] ]
| Some (Some r) -> [ span [ txt @@ Printf.sprintf "recovery set as %s" r ] ]))
| BatchNot b ->
div ~a:[ a_class [ Display.d_flex; Flex.flex_column ] ] (
List.map (fun not_op -> notif_kind_to_row network
(OpNot {b with not_op})) b.not_op)
| ApprovNot n ->
let icon =
match n.not_approv_icon with None -> "FILLER.PNG" | Some icon -> icon in
......
......@@ -402,7 +402,7 @@ div.d-none * {
font-size: x-large ;
}
#amount .currency img {
.currency img {
height:50px ;
}
......
......@@ -54,6 +54,59 @@ function post (method, cb, data) {
window.postMessage({ src: "inpage", req: request }, "*")
}
function makeData (kind, data) {
if (kind == "transaction") {
return {
kind: kind,
info: {
fee: data.fee,
gasLimit: data.gas_limit,
storageLimit: data.storage_limit
},
transactionDetails: {
destination: data.dst,
amount: data.amount,
parameters: {
entrypoint: data.entrypoint,
value: data.parameter
}
}
};
} else if (kind == "origination") {
return {
kind: kind,
info: {
fee: data.fee,
gasLimit: data.gas_limit,
storageLimit: data.storage_limit
},
originationDetails: {
balance: data.balance,
script: {
code: data.sc_code,
codeHash: data.sc_code_hash,
storage: data.sc_storage
}
}
}
} else if (kind == "delegation") {
return {
kind: kind,
info: {
fee: data.fee,
gasLimit: data.gas_limit,
storageLimit: data.storage_limit
},
delegationDetails: {
delegate: data.delegate
}
}
} else {
console.log("[inpage] type of operation not handled", kind)
}
}
// metal state = { enabled ; unlocked ; approved ; selected_account }
// onstatechanged_listener : trigger when state is changed
......@@ -152,22 +205,9 @@ metal = {
* @param {hashCallback} obj.cb - callback with the hash of the injected operation as param
*/
send: function({dst, amount, fee, entrypoint, parameter, gas_limit, storage_limit, cb}) {
var data = {
kind: "transaction",
info: {
fee: fee,
gasLimit: gas_limit,
storageLimit: storage_limit
},
transactionDetails: {
destination: dst,
amount: amount,
parameters: {
entrypoint: entrypoint,
value: parameter
}
}
}
var data = makeData(
"transaction",
{dst, amount, fee, entrypoint, parameter, gas_limit, storage_limit});
post("send", cb, data)
},
......@@ -186,22 +226,9 @@ metal = {
*/
originate: function({balance, fee, gas_limit, storage_limit,
sc_code_hash, sc_storage, sc_code, cb}) {
var data = {
kind: "origination",
info: {
fee: fee,
gasLimit: gas_limit,
storageLimit: storage_limit
},
originationDetails: {
balance: balance,
script: {
code: sc_code,
codeHash: sc_code_hash,
storage: sc_storage
}
}
}
var data = makeData(
"origination",
{balance, fee, gas_limit, storage_limit, sc_code_hash, sc_storage, sc_code});
post("originate", cb, data)
},
......@@ -216,17 +243,9 @@ metal = {
* @param {hashCallback} obj.cb - callback with the hash of the injected operation as param
*/
delegate: function({delegate, fee, gas_limit, storage_limit, cb}) {
var data = {
kind: "delegation",
info: {
fee: fee,
gasLimit: gas_limit,
storageLimit: storage_limit
},
delegationDetails: {
delegate: delegate
}
}
var data = makeData(
"delegation",
{delegate, fee, gas_limit, storage_limit});
post("delegate", cb, data)
},
......@@ -239,6 +258,22 @@ metal = {
*/
onStateChanged: function (cb) {
state_cb = cb
}
},
/**
* Batched operations
* @function batch
* @param {Object} obj
* @param {Object[]} obj.operations
* @param {string} obj.operations[].kind - kind of operation
* @param {Object} obj.operations[].operation - operation (send, originate or delegate) as described before
* @param {hashCallback} obj.cb - callback with the hash of the injected operation as param
*/
batch: function({operations, cb}) {
function make(op) { return makeData(op.kind, op.operation) };
var data = operations.map(make);
post("batch", cb, data)
},
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment