JJ: Description from the destination commit:

+ make_denom_group

JJ: Description from source commit:
ok denom group + exchange_sig
This commit is contained in:
swrup 2026-02-23 09:02:39 +01:00
parent 72b0c3012a
commit f6e9986aa5
3 changed files with 132 additions and 79 deletions

View file

@ -1229,10 +1229,6 @@ module ExchangeKeysResponse = struct
hard_limits: AccountLimit.t list; hard_limits: AccountLimit.t list;
zero_limits: ZeroLimitedOperation.t list; zero_limits: ZeroLimitedOperation.t list;
denominations: DenomGroup.t list; denominations: DenomGroup.t list;
(* Compact EdDSA signature (binary-only) over the
contatentation of all of the master_sigs (in reverse
chronological order by group) in the arrays under
"denominations" *)
exchange_sig: ExchangeKeySet.t; exchange_sig: ExchangeKeySet.t;
exchange_pub: EddsaPublicKey.t; exchange_pub: EddsaPublicKey.t;
recoup: RecoupDenoms.t list; recoup: RecoupDenoms.t list;

View file

@ -15,3 +15,98 @@ type t = {
h_pub: denom_hash; h_pub: denom_hash;
master_sig: Signatures.DenominationKeyValidity.t; master_sig: Signatures.DenominationKeyValidity.t;
} }
let make_denom_group_sorted denominations =
let open Syntax in
let ht = Hashtbl.create 0xff in
List.iter
(fun coin ->
let open Config.Coin in
assert (coin.cipher = `RSA);
let k =
( coin.value,
coin.fee_withdraw,
coin.fee_deposit,
coin.fee_refresh,
coin.fee_refund )
in
let v = [] in
Hashtbl.replace ht k v)
Config.Coin.all_coins;
let+ () =
list_iter
(fun {
pub;
value;
stamp_start;
stamp_expire_withdraw;
stamp_expire_deposit;
stamp_expire_legal;
fee_withdraw;
fee_deposit;
fee_refresh;
fee_refund;
age_mask= _;
h_pub= _;
master_sig;
} ->
let k = (value, fee_withdraw, fee_deposit, fee_refresh, fee_refund) in
let v =
Api.RsaDenom.
{
rsa_pub= pub;
master_sig;
stamp_start;
stamp_expire_withdraw;
stamp_expire_deposit;
stamp_expire_legal;
lost= None;
}
in
let+ l =
match Hashtbl.find_opt ht k with
| None ->
Error "denomination does not match any coin in configuration"
| Some l -> Ok l
in
Hashtbl.replace ht k (v :: l))
denominations
in
(* ! important for /keys .exchange_sig
each group must be ordered in _reverse_ chronological order
order of groups to compute hash, must be the same as the order
in /keys .denominations *)
let open Api in
let open DenomGroup in
let open RsaDenomGroup in
let open RsaDenom in
let cmp_denom b a = Timestamp.compare a.stamp_start b.stamp_start in
let cmp_group =
let cmp_amount a b =
let open Amount in
match Int64.unsigned_compare a.value b.value with
| 0 -> Int32.unsigned_compare a.fraction b.fraction
| n -> n
in
fun (Rsa a) (Rsa b) -> cmp_amount a.value b.value
in
let l =
Hashtbl.to_seq ht
|> List.of_seq
|> List.map
(fun
((value, fee_withdraw, fee_deposit, fee_refresh, fee_refund), denoms)
->
Rsa
{
denoms= List.sort cmp_denom denoms;
value;
fee_withdraw;
fee_deposit;
fee_refresh;
fee_refund;
})
|> List.sort cmp_group
in
l

View file

@ -20,44 +20,6 @@ let config req _server _env =
let res = Api.encode Api.ExchangeVersionResponse.jsont config in let res = Api.encode Api.ExchangeVersionResponse.jsont config in
Respond.result res req Respond.result res req
(* TODO
for now we only have one item in each "denom group"
change this once we have denom/signkey rotation *)
let denomgroup_of_denomdata
Denomination.
{
pub;
value;
stamp_start;
stamp_expire_withdraw;
stamp_expire_deposit;
stamp_expire_legal;
fee_withdraw;
fee_deposit;
fee_refresh;
fee_refund;
age_mask= _;
h_pub= _;
master_sig;
} =
let denoms =
[
RsaDenom.
{
rsa_pub= pub;
master_sig;
stamp_start;
stamp_expire_withdraw;
stamp_expire_deposit;
stamp_expire_legal;
lost= None;
};
]
in
DenomGroup.Rsa
RsaDenomGroup.
{ denoms; value; fee_withdraw; fee_deposit; fee_refresh; fee_refund }
let mk_keys ~db_conn (module Keys : Keys.S) ~last_issue_date = let mk_keys ~db_conn (module Keys : Keys.S) ~last_issue_date =
let version = Api.protocol_version in let version = Api.protocol_version in
let base_url = Config.base_url in let base_url = Config.base_url in
@ -96,63 +58,65 @@ let mk_keys ~db_conn (module Keys : Keys.S) ~last_issue_date =
let wallet_balance_limit_without_kyc = None in let wallet_balance_limit_without_kyc = None in
let hard_limits = [] in let hard_limits = [] in
let zero_limits = [] in let zero_limits = [] in
let* dn_l =
let+ l = Keys.denominations () in let* denom_l = Keys.denominations () in
let denom_l =
(* reverse chronological order *) (* reverse chronological order *)
List.sort List.sort
(fun a b -> Stdlib.compare b.Denomination.stamp_start a.stamp_start) (fun a b -> Stdlib.compare b.Denomination.stamp_start a.stamp_start)
l denom_l
in in
let list_issue_date = let list_issue_date =
match dn_l with match denom_l with
| [] -> Timestamp.never | [] -> Timestamp.zero
| dn :: _ -> dn.Denomination.stamp_start | dn :: _ -> dn.Denomination.stamp_start
in in
let denominations = let denom_l =
(* if `?last_issue_date` query param does not exactly match the `stamp_start` (* if `?last_issue_date` query param does not exactly match the `stamp_start`
of one of the denomination keys, all keys are returned *) of one of the denomination keys, all keys are returned *)
let open Denomination in let stamp_start_opt =
let l =
match last_issue_date with match last_issue_date with
| None -> dn_l | None -> None
| Some last_issue_date -> ( | Some timestamp ->
match List.find_map
List.find_opt (fun v ->
(fun v -> Timestamp.compare v.stamp_start last_issue_date = 0) if Timestamp.compare v.Denomination.stamp_start timestamp = 0 then
dn_l Some v.stamp_start
with else None)
| None -> dn_l denom_l
| Some _ ->
List.filter
(fun v ->
Time.Timestamp.compare v.stamp_start last_issue_date >= 0)
dn_l)
in in
List.map denomgroup_of_denomdata l match stamp_start_opt with
| None -> denom_l
| Some timestamp ->
List.filter
(fun v -> Timestamp.compare v.Denomination.stamp_start timestamp >= 0)
denom_l
in in
let* denominations = Denomination.make_denom_group_sorted denom_l in
let* signkeys = let* signkeys = Keys.signkeys () in
let+ l = Keys.signkeys () in let signkeys =
l signkeys
|> List.sort (fun a b -> |> List.sort (fun a b ->
let open Signkey in let open Signkey in
Stdlib.compare b.stamp_start a.stamp_start) Stdlib.compare b.stamp_start a.stamp_start)
|> List.map Api.SignKey.of_signkey |> List.map Api.SignKey.of_signkey
in in
let exchange_pub = (* the eddsa pub key used to sign exchange_sig *)
(* the eddsa pub key used to sign exchange_sig *) let* exchange_pub =
match signkeys with match signkeys with
| [] -> Fmt.failwith "exchange has no active signkey" | [] -> Fmt.error "exchange has no active signkey"
| sk :: _ -> sk.SignKey.key | sk :: _ -> Ok sk.SignKey.key
in in
(* eddsa signature over the contatentation of all of the master_sigs
(in reverse chronological order by group) in the arrays under "denominations" *)
let exchange_sig = let exchange_sig =
(* Compact EdDSA signature (binary-only) over the
contatentation of all of the master_sigs (in reverse
chronological order by group) in the arrays under "denominations". *)
let hc = let hc =
dn_l denominations
|> List.map (fun dn -> dn.Denomination.master_sig) |> List.map (fun (DenomGroup.Rsa g) -> g.RsaDenomGroup.denoms)
|> List.concat_map (List.map (fun dn -> dn.RsaDenom.master_sig))
|> List.map Signatures.DenominationKeyValidity.to_octets |> List.map Signatures.DenominationKeyValidity.to_octets
|> String.concat "" |> String.concat ""
|> Hash.H64.hash |> Hash.H64.hash
@ -222,9 +186,7 @@ let keys req server _env =
| [] -> Ok None | [] -> Ok None
| s :: _ -> ( | s :: _ -> (
match Int64.of_string_opt s with match Int64.of_string_opt s with
| None -> | None -> Error "invalid `?last_issue_date` query param, not an int"
Error
"invalid `?last_issue_date` query param, int_of_string failure"
| Some n -> Ok (Some (Timestamp.of_s n))) | Some n -> Ok (Some (Timestamp.of_s n)))
in in
let* v = mk_keys ~db_conn keys ~last_issue_date in let* v = mk_keys ~db_conn keys ~last_issue_date in