Add high watermark checking for blocks and endorsements
This commit is contained in:
parent
4a436c2f18
commit
96c36f1698
@ -27,6 +27,63 @@ open Signer_logging
|
||||
|
||||
let log = lwt_log_notice
|
||||
|
||||
module High_watermark = struct
|
||||
let encoding =
|
||||
let open Data_encoding in
|
||||
let raw_hash =
|
||||
conv Blake2B.to_bytes Blake2B.of_bytes_exn bytes in
|
||||
conv
|
||||
(List.map (fun (chain_id, marks) -> Chain_id.to_b58check chain_id, marks))
|
||||
(List.map (fun (chain_id, marks) -> Chain_id.of_b58check_exn chain_id, marks)) @@
|
||||
assoc @@
|
||||
conv
|
||||
(List.map (fun (pkh, mark) -> Signature.Public_key_hash.to_b58check pkh, mark))
|
||||
(List.map (fun (pkh, mark) -> Signature.Public_key_hash.of_b58check_exn pkh, mark)) @@
|
||||
assoc @@
|
||||
obj2
|
||||
(req "level" int32)
|
||||
(req "hash" raw_hash)
|
||||
|
||||
let mark_if_block_or_endorsement (cctxt : #Client_context.wallet) pkh bytes =
|
||||
let mark art name get_level =
|
||||
let file = name ^ "_high_watermark" in
|
||||
cctxt#with_lock @@ fun () ->
|
||||
cctxt#load file ~default:[] encoding >>=? fun all ->
|
||||
if MBytes.length bytes < 9 then
|
||||
failwith "byte sequence too short to be %s %s" art name
|
||||
else
|
||||
let hash = Blake2B.hash_bytes [ bytes ] in
|
||||
let chain_id = Chain_id.of_bytes_exn (MBytes.sub bytes 1 4) in
|
||||
let level = get_level () in
|
||||
begin match List.assoc_opt chain_id all with
|
||||
| None -> return ()
|
||||
| Some marks ->
|
||||
match List.assoc_opt pkh marks with
|
||||
| None -> return ()
|
||||
| Some (previous_level, previous_hash) ->
|
||||
if previous_level > level then
|
||||
failwith "%s level %ld below high watermark %ld" name level previous_level
|
||||
else if previous_level = level && previous_hash <> hash then
|
||||
failwith "%s level %ld already signed with a different header" name level
|
||||
else
|
||||
return ()
|
||||
end >>=? fun () ->
|
||||
let rec update = function
|
||||
| [] -> [ chain_id, [ pkh, (level, hash) ] ]
|
||||
| (e_chain_id, marks) :: rest ->
|
||||
if chain_id = e_chain_id then
|
||||
let marks = (pkh, (level, hash)) :: List.filter (fun (pkh', _) -> pkh <> pkh') marks in
|
||||
(e_chain_id, marks) :: rest
|
||||
else
|
||||
(e_chain_id, marks) :: update rest in
|
||||
cctxt#write file (update all) encoding in
|
||||
if MBytes.length bytes > 0 && MBytes.get_uint8 bytes 0 = 0x01 then
|
||||
mark "a" "block" (fun () -> MBytes.get_int32 bytes 5)
|
||||
else if MBytes.length bytes > 0 && MBytes.get_uint8 bytes 0 = 0x02 then
|
||||
mark "an" "endorsement" (fun () -> MBytes.get_int32 bytes (MBytes.length bytes - 4))
|
||||
else return ()
|
||||
|
||||
end
|
||||
|
||||
module Authorized_key =
|
||||
Client_aliases.Alias (struct
|
||||
@ -50,7 +107,7 @@ let check_magic_byte magic_bytes data =
|
||||
let sign
|
||||
(cctxt : #Client_context.wallet)
|
||||
Signer_messages.Sign.Request.{ pkh ; data ; signature }
|
||||
?magic_bytes ~require_auth =
|
||||
?magic_bytes ~check_high_watermark ~require_auth =
|
||||
log Tag.DSL.(fun f ->
|
||||
f "Request for signing %d bytes of data for key %a, magic byte = %02X"
|
||||
-% t event "request_for_signing"
|
||||
@ -77,6 +134,10 @@ let sign
|
||||
f "Signing data for key %s"
|
||||
-% t event "signing_data"
|
||||
-% s Client_keys.Logging.tag name) >>= fun () ->
|
||||
begin if check_high_watermark then
|
||||
High_watermark.mark_if_block_or_endorsement cctxt pkh data
|
||||
else return ()
|
||||
end >>=? fun () ->
|
||||
Client_keys.sign cctxt sk_uri data >>=? fun signature ->
|
||||
return signature
|
||||
|
||||
|
@ -26,11 +26,11 @@
|
||||
let log = Signer_logging.lwt_log_notice
|
||||
open Signer_logging
|
||||
|
||||
let run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes ~require_auth mode =
|
||||
let run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes ~check_high_watermark ~require_auth mode =
|
||||
let dir = RPC_directory.empty in
|
||||
let dir =
|
||||
RPC_directory.register1 dir Signer_services.sign begin fun pkh signature data ->
|
||||
Handler.sign cctxt { pkh ; data ; signature } ?magic_bytes ~require_auth
|
||||
Handler.sign cctxt { pkh ; data ; signature } ?magic_bytes ~check_high_watermark ~require_auth
|
||||
end in
|
||||
let dir =
|
||||
RPC_directory.register1 dir Signer_services.public_key begin fun pkh () () ->
|
||||
@ -63,7 +63,7 @@ let run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes ~require_auth mode
|
||||
failwith "Port already in use."
|
||||
| exn -> Lwt.return (error_exn exn))
|
||||
|
||||
let run_https (cctxt : #Client_context.wallet) ~host ~port ~cert ~key ?magic_bytes ~require_auth =
|
||||
let run_https (cctxt : #Client_context.wallet) ~host ~port ~cert ~key ?magic_bytes ~check_high_watermark ~require_auth =
|
||||
Lwt_utils_unix.getaddrinfo ~passive:true ~node:host ~service:(string_of_int port) >>= function
|
||||
| []->
|
||||
failwith "Cannot resolve listening address: %S" host
|
||||
@ -75,9 +75,9 @@ let run_https (cctxt : #Client_context.wallet) ~host ~port ~cert ~key ?magic_byt
|
||||
-% s port_number port) >>= fun () ->
|
||||
let mode : Conduit_lwt_unix.server =
|
||||
`TLS (`Crt_file_path cert, `Key_file_path key, `No_password, `Port port) in
|
||||
run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes ~require_auth mode
|
||||
run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes ~check_high_watermark ~require_auth mode
|
||||
|
||||
let run_http (cctxt : #Client_context.wallet) ~host ~port ?magic_bytes ~require_auth =
|
||||
let run_http (cctxt : #Client_context.wallet) ~host ~port ?magic_bytes ~check_high_watermark ~require_auth =
|
||||
Lwt_utils_unix.getaddrinfo ~passive:true ~node:host ~service:(string_of_int port) >>= function
|
||||
| [] ->
|
||||
failwith "Cannot resolve listening address: %S" host
|
||||
@ -89,4 +89,4 @@ let run_http (cctxt : #Client_context.wallet) ~host ~port ?magic_bytes ~require_
|
||||
-% s port_number port) >>= fun () ->
|
||||
let mode : Conduit_lwt_unix.server =
|
||||
`TCP (`Port port) in
|
||||
run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes ~require_auth mode
|
||||
run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes ~check_high_watermark ~require_auth mode
|
||||
|
@ -27,6 +27,7 @@ val run_https:
|
||||
#Client_context.io_wallet ->
|
||||
host:string -> port:int -> cert:string -> key:string ->
|
||||
?magic_bytes: int list ->
|
||||
check_high_watermark: bool ->
|
||||
require_auth: bool ->
|
||||
'a tzresult Lwt.t
|
||||
|
||||
@ -34,5 +35,6 @@ val run_http:
|
||||
#Client_context.io_wallet ->
|
||||
host:string -> port:int ->
|
||||
?magic_bytes: int list ->
|
||||
check_high_watermark: bool ->
|
||||
require_auth: bool ->
|
||||
'a tzresult Lwt.t
|
||||
|
@ -82,13 +82,25 @@ let magic_bytes_arg =
|
||||
failwith "Bad format for magic bytes, a series of numbers \
|
||||
is expected, separated by commas."))
|
||||
|
||||
let high_watermark_switch =
|
||||
Clic.switch
|
||||
~doc: "high watermark restriction\n\
|
||||
Stores the highest level signed for blocks and endorsements \
|
||||
for each address, and forbids to sign a level that is \
|
||||
inferior or equal afterwards, except for the exact same \
|
||||
input data."
|
||||
~short: 'W'
|
||||
~long: "check-high-watermark"
|
||||
()
|
||||
|
||||
let commands base_dir require_auth =
|
||||
Client_keys_commands.commands None @
|
||||
Tezos_signer_backends.Ledger.commands () @
|
||||
[ command ~group
|
||||
~desc: "Launch a signer daemon over a TCP socket."
|
||||
(args3
|
||||
(args4
|
||||
magic_bytes_arg
|
||||
high_watermark_switch
|
||||
(default_arg
|
||||
~doc: "listening address or host name"
|
||||
~short: 'a'
|
||||
@ -104,16 +116,17 @@ let commands base_dir require_auth =
|
||||
~default: default_tcp_port
|
||||
(parameter (fun _ s -> return s))))
|
||||
(prefixes [ "launch" ; "socket" ; "signer" ] @@ stop)
|
||||
(fun (magic_bytes, host, port) cctxt ->
|
||||
(fun (magic_bytes, check_high_watermark, host, port) cctxt ->
|
||||
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
||||
Socket_daemon.run
|
||||
cctxt (Tcp (host, port, [AI_SOCKTYPE SOCK_STREAM]))
|
||||
?magic_bytes ~require_auth >>=? fun _ ->
|
||||
?magic_bytes ~check_high_watermark ~require_auth >>=? fun _ ->
|
||||
return_unit) ;
|
||||
command ~group
|
||||
~desc: "Launch a signer daemon over a local Unix socket."
|
||||
(args2
|
||||
(args3
|
||||
magic_bytes_arg
|
||||
high_watermark_switch
|
||||
(default_arg
|
||||
~doc: "path to the local socket file"
|
||||
~short: 's'
|
||||
@ -122,15 +135,16 @@ let commands base_dir require_auth =
|
||||
~default: (Filename.concat base_dir "socket")
|
||||
(parameter (fun _ s -> return s))))
|
||||
(prefixes [ "launch" ; "local" ; "signer" ] @@ stop)
|
||||
(fun (magic_bytes, path) cctxt ->
|
||||
(fun (magic_bytes, check_high_watermark, path) cctxt ->
|
||||
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
||||
Socket_daemon.run
|
||||
cctxt (Unix path) ?magic_bytes ~require_auth >>=? fun _ ->
|
||||
cctxt (Unix path) ?magic_bytes ~check_high_watermark ~require_auth >>=? fun _ ->
|
||||
return_unit) ;
|
||||
command ~group
|
||||
~desc: "Launch a signer daemon over HTTP."
|
||||
(args3
|
||||
(args4
|
||||
magic_bytes_arg
|
||||
high_watermark_switch
|
||||
(default_arg
|
||||
~doc: "listening address or host name"
|
||||
~short: 'a'
|
||||
@ -149,13 +163,14 @@ let commands base_dir require_auth =
|
||||
try return (int_of_string x)
|
||||
with Failure _ -> failwith "Invalid port %s" x))))
|
||||
(prefixes [ "launch" ; "http" ; "signer" ] @@ stop)
|
||||
(fun (magic_bytes, host, port) cctxt ->
|
||||
(fun (magic_bytes, check_high_watermark, host, port) cctxt ->
|
||||
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
||||
Http_daemon.run_http cctxt ~host ~port ?magic_bytes ~require_auth) ;
|
||||
Http_daemon.run_http cctxt ~host ~port ?magic_bytes ~check_high_watermark ~require_auth) ;
|
||||
command ~group
|
||||
~desc: "Launch a signer daemon over HTTPS."
|
||||
(args3
|
||||
(args4
|
||||
magic_bytes_arg
|
||||
high_watermark_switch
|
||||
(default_arg
|
||||
~doc: "listening address or host name"
|
||||
~short: 'a'
|
||||
@ -190,9 +205,9 @@ let commands base_dir require_auth =
|
||||
failwith "No such TLS key file %s" s
|
||||
else
|
||||
return s)) @@ stop)
|
||||
(fun (magic_bytes, host, port) cert key cctxt ->
|
||||
(fun (magic_bytes, check_high_watermark, host, port) cert key cctxt ->
|
||||
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
||||
Http_daemon.run_https cctxt ~host ~port ~cert ~key ?magic_bytes ~require_auth) ;
|
||||
Http_daemon.run_https cctxt ~host ~port ~cert ~key ?magic_bytes ~check_high_watermark ~require_auth) ;
|
||||
command ~group
|
||||
~desc: "Authorize a given public key to perform signing requests."
|
||||
(args1
|
||||
|
@ -28,11 +28,11 @@ open Signer_messages
|
||||
|
||||
let log = lwt_log_notice
|
||||
|
||||
let handle_client ?magic_bytes ~require_auth cctxt fd =
|
||||
let handle_client ?magic_bytes ~check_high_watermark ~require_auth cctxt fd =
|
||||
Lwt_utils_unix.Socket.recv fd Request.encoding >>=? function
|
||||
| Sign req ->
|
||||
let encoding = result_encoding Sign.Response.encoding in
|
||||
Handler.sign cctxt req ?magic_bytes ~require_auth >>= fun res ->
|
||||
Handler.sign cctxt req ?magic_bytes ~check_high_watermark ~require_auth >>= fun res ->
|
||||
Lwt_utils_unix.Socket.send fd encoding res >>= fun _ ->
|
||||
Lwt_unix.close fd >>= fun () ->
|
||||
return_unit
|
||||
@ -54,7 +54,7 @@ let handle_client ?magic_bytes ~require_auth cctxt fd =
|
||||
Lwt_unix.close fd >>= fun () ->
|
||||
return_unit
|
||||
|
||||
let run (cctxt : #Client_context.wallet) path ?magic_bytes ~require_auth =
|
||||
let run (cctxt : #Client_context.wallet) path ?magic_bytes ~check_high_watermark ~require_auth =
|
||||
let open Lwt_utils_unix.Socket in
|
||||
begin
|
||||
match path with
|
||||
@ -84,7 +84,7 @@ let run (cctxt : #Client_context.wallet) path ?magic_bytes ~require_auth =
|
||||
| [Exn End_of_file] -> return_unit
|
||||
| errs -> Lwt.return (Error errs))
|
||||
(fun () ->
|
||||
handle_client ?magic_bytes ~require_auth cctxt cfd)
|
||||
handle_client ?magic_bytes ~check_high_watermark ~require_auth cctxt cfd)
|
||||
end ;
|
||||
loop fd
|
||||
in
|
||||
|
@ -27,5 +27,6 @@ val run:
|
||||
#Client_context.io_wallet ->
|
||||
Lwt_utils_unix.Socket.addr ->
|
||||
?magic_bytes: int list ->
|
||||
check_high_watermark: bool ->
|
||||
require_auth: bool ->
|
||||
'a list tzresult Lwt.t
|
||||
|
Loading…
Reference in New Issue
Block a user