Merge branch 'webide/id-example' into 'dev'

[LIGO-499] Add ID layer contract to web IDE examples

See merge request ligolang/ligo!451
This commit is contained in:
Jev Björsell 2020-06-02 16:45:29 +00:00
commit 13eae4be18
13 changed files with 2368 additions and 179 deletions

159
src/test/contracts/id.ligo Normal file
View File

@ -0,0 +1,159 @@
type id is int
type id_details is
record [
owner: address;
controller: address;
profile: bytes;
]
type buy is
record [
profile: bytes;
initial_controller: option(address);
]
type update_owner is
record [
id: id;
new_owner: address;
]
type update_details is
record [
id: id;
new_profile: option(bytes);
new_controller: option(address);
]
type action is
| Buy of buy
| Update_owner of update_owner
| Update_details of update_details
| Skip of unit
(* The prices kept in storage can be changed by bakers, though they should only be
adjusted down over time, not up. *)
type storage is
record [
identities: big_map (id, id_details);
next_id: int;
name_price: tez;
skip_price: tez;
]
(** Preliminary thoughts on ids:
I very much like the simplicity of http://gurno.com/adam/mne/.
5 three letter words means you have a 15 character identity, not actually more
annoying than an IP address and a lot more memorable than the raw digits. This
can be stored as a single integer which is then translated into the corresponding
series of 5 words.
I in general like the idea of having a 'skip' mechanism, but it does need to cost
something so people don't eat up the address space. 256 ^ 5 means you have a lot
of address space, but if people troll by skipping a lot that could be eaten up.
Should probably do some napkin calculations for how expensive skipping needs to
be to deter people from doing it just to chew up address space.
*)
function buy (const parameter : buy; const storage : storage) : list(operation) * storage is
begin
if amount = storage.name_price
then skip
else failwith("Incorrect amount paid.");
const profile : bytes = parameter.profile;
const initial_controller : option(address) = parameter.initial_controller;
var identities : big_map (id, id_details) := storage.identities;
const new_id : int = storage.next_id;
const controller : address =
case initial_controller of
Some(addr) -> addr
| None -> sender
end;
const new_id_details: id_details =
record [
owner = sender ;
controller = controller ;
profile = profile ;
];
identities[new_id] := new_id_details;
end with ((nil : list(operation)), storage with record [
identities = identities;
next_id = new_id + 1;
])
function update_owner (const parameter : update_owner; const storage : storage) :
list(operation) * storage is
begin
if (amount =/= 0mutez)
then
begin
failwith("Updating owner doesn't cost anything.");
end
else skip;
const id : int = parameter.id;
const new_owner : address = parameter.new_owner;
var identities : big_map (id, id_details) := storage.identities;
const id_details : id_details =
case identities[id] of
Some(id_details) -> id_details
| None -> (failwith("This ID does not exist."): id_details)
end;
if sender = id_details.owner
then skip;
else failwith("You are not the owner of this ID.");
id_details.owner := new_owner;
identities[id] := id_details;
end with ((nil: list(operation)), storage with record [ identities = identities; ])
function update_details (const parameter : update_details; const storage : storage ) :
list(operation) * storage is
begin
if (amount =/= 0mutez)
then failwith("Updating details doesn't cost anything.")
else skip;
const id : int = parameter.id;
const new_profile : option(bytes) = parameter.new_profile;
const new_controller : option(address) = parameter.new_controller;
const identities : big_map (id, id_details) = storage.identities;
const id_details: id_details =
case identities[id] of
Some(id_details) -> id_details
| None -> (failwith("This ID does not exist."): id_details)
end;
if (sender = id_details.controller) or (sender = id_details.owner)
then skip;
else failwith("You are not the owner or controller of this ID.");
const owner: address = id_details.owner;
const profile: bytes =
case new_profile of
None -> (* Default *) id_details.profile
| Some(new_profile) -> new_profile
end;
const controller: address =
case new_controller of
None -> (* Default *) id_details.controller
| Some(new_controller) -> new_controller
end;
id_details.owner := owner;
id_details.controller := controller;
id_details.profile := profile;
identities[id] := id_details;
end with ((nil: list(operation)), storage with record [ identities = identities; ])
(* Let someone skip the next identity so nobody has to take one that's undesirable *)
function skip_ (const p: unit; const storage: storage) : list(operation) * storage is
begin
if amount = storage.skip_price
then skip
else failwith("Incorrect amount paid.");
end with ((nil: list(operation)), storage with record [ next_id = storage.next_id + 1; ])
function main (const action : action; const storage : storage) : list(operation) * storage is
case action of
| Buy(b) -> buy (b, storage)
| Update_owner(uo) -> update_owner (uo, storage)
| Update_details(ud) -> update_details (ud, storage)
| Skip(s) -> skip_ (unit, storage)
end;

View File

@ -6,9 +6,21 @@ type id_details = {
profile: bytes profile: bytes
} }
type buy = bytes * address option type buy = {
type update_owner = id * address profile: bytes;
type update_details = id * bytes option * address option initial_controller: address option;
}
type update_owner = {
id: id;
new_owner: address;
}
type update_details = {
id: id;
new_profile: bytes option;
new_controller: address option;
}
type action = type action =
| Buy of buy | Buy of buy
@ -19,7 +31,14 @@ type action =
(* The prices kept in storage can be changed by bakers, though they (* The prices kept in storage can be changed by bakers, though they
should only be adjusted down over time, not up. *) should only be adjusted down over time, not up. *)
type storage = (id, id_details) big_map * int * (tez * tez) (* The prices kept in storage can be changed by bakers, though they should only be
adjusted down over time, not up. *)
type storage = {
identities: (id, id_details) big_map;
next_id: int;
name_price: tez;
skip_price: tez;
}
type return = operation list * storage type return = operation list * storage
@ -38,13 +57,17 @@ a lot that could be eaten up. Should probably do some napkin
calculations for how expensive skipping needs to be to deter people calculations for how expensive skipping needs to be to deter people
from doing it just to chew up address space. *) from doing it just to chew up address space. *)
let buy (parameter, storage: (bytes * address option) * storage) = let buy (parameter, storage: buy * storage) =
let void : unit = let void: unit =
if Tezos.amount <> storage.2.0 if amount = storage.name_price
then (failwith "Incorrect amount paid.": unit) in then ()
let profile, initial_controller = parameter in else (failwith "Incorrect amount paid.": unit)
let identities, new_id, prices = storage in in
let controller : address = let profile = parameter.profile in
let initial_controller = parameter.initial_controller in
let identities = storage.identities in
let new_id = storage.next_id in
let controller: address =
match initial_controller with match initial_controller with
| Some addr -> addr | Some addr -> addr
| None -> sender in | None -> sender in
@ -54,74 +77,84 @@ let buy (parameter, storage: (bytes * address option) * storage) =
profile = profile} in profile = profile} in
let updated_identities : (id, id_details) big_map = let updated_identities : (id, id_details) big_map =
Big_map.update new_id (Some new_id_details) identities Big_map.update new_id (Some new_id_details) identities
in ([]: operation list), (updated_identities, new_id + 1, prices) in
([]: operation list), {storage with identities = updated_identities;
next_id = new_id + 1;
}
let update_owner (parameter, storage : (id * address) * storage) = let update_owner (parameter, storage: update_owner * storage) =
if amount <> 0tez if (amount <> 0mutez)
then (failwith "Updating owner doesn't cost anything.": return) then (failwith "Updating owner doesn't cost anything.": (operation list) * storage)
else else
let id, new_owner = parameter in let id = parameter.id in
let identities, last_id, prices = storage in let new_owner = parameter.new_owner in
let current_id_details : id_details = let identities = storage.identities in
match Big_map.find_opt id identities with let current_id_details: id_details =
| Some id_details -> id_details match Big_map.find_opt id identities with
| None -> (failwith "This ID does not exist." : id_details) in | Some id_details -> id_details
let is_allowed : bool = | None -> (failwith "This ID does not exist.": id_details)
if Tezos.sender = current_id_details.owner in
then true let u : unit =
else (failwith "You are not the owner of this ID." : bool) in if sender = current_id_details.owner
let updated_id_details : id_details = { then ()
else failwith "You are not the owner of this ID."
in
let updated_id_details: id_details = {
owner = new_owner; owner = new_owner;
controller = current_id_details.controller; controller = current_id_details.controller;
profile = current_id_details.profile} in profile = current_id_details.profile;
let updated_identities = }
Big_map.update id (Some updated_id_details) identities in
in ([]: operation list), (updated_identities, last_id, prices) let updated_identities = Big_map.update id (Some updated_id_details) identities in
([]: operation list), {storage with identities = updated_identities}
let update_details (parameter, storage: (id * bytes option * address option) * storage) = let update_details (parameter, storage: update_details * storage) =
if Tezos.amount <> 0tez if (amount <> 0mutez)
then then (failwith "Updating details doesn't cost anything.": (operation list) * storage)
(failwith "Updating details doesn't cost anything." : return)
else else
let id, new_profile, new_controller = parameter in let id = parameter.id in
let identities, last_id, prices = storage in let new_profile = parameter.new_profile in
let current_id_details: id_details = let new_controller = parameter.new_controller in
match Big_map.find_opt id identities with let identities = storage.identities in
| Some id_details -> id_details let current_id_details: id_details =
| None -> (failwith "This ID does not exist.": id_details) in match Big_map.find_opt id identities with
let is_allowed : bool = | Some id_details -> id_details
if Tezos.sender = current_id_details.controller | None -> (failwith "This ID does not exist.": id_details)
|| Tezos.sender = current_id_details.owner in
then true let u : unit =
else if (sender = current_id_details.controller) || (sender = current_id_details.owner)
(failwith ("You are not the owner or controller of this ID.") then ()
: bool) in else failwith ("You are not the owner or controller of this ID.")
let owner : address = current_id_details.owner in in
let profile : bytes = let owner: address = current_id_details.owner in
match new_profile with let profile: bytes =
| None -> (* Default *) current_id_details.profile match new_profile with
| Some new_profile -> new_profile in | None -> (* Default *) current_id_details.profile
let controller : address = | Some new_profile -> new_profile
match new_controller with in
| None -> (* Default *) current_id_details.controller let controller: address =
| Some new_controller -> new_controller in match new_controller with
let updated_id_details: id_details = { | None -> (* Default *) current_id_details.controller
owner = owner; | Some new_controller -> new_controller
controller = controller; in
profile = profile} in let updated_id_details: id_details = {
owner = owner;
controller = controller;
profile = profile;
}
in
let updated_identities: (id, id_details) big_map = let updated_identities: (id, id_details) big_map =
Big_map.update id (Some updated_id_details) identities Big_map.update id (Some updated_id_details) identities in
in ([]: operation list), (updated_identities, last_id, prices) ([]: operation list), {storage with identities = updated_identities}
(* Let someone skip the next identity so nobody has to take one that's (* Let someone skip the next identity so nobody has to take one that's undesirable *)
undesirable *) let skip (p,storage: unit * storage) =
let void: unit =
let skip (p, storage: unit * storage) = if amount = storage.skip_price
let void : unit = then ()
if Tezos.amount <> storage.2.1 else failwith "Incorrect amount paid."
then (failwith "Incorrect amount paid." : unit) in in
let identities, last_id, prices = storage in ([]: operation list), {storage with next_id = storage.next_id + 1}
([]: operation list), (identities, last_id + 1, prices)
let main (action, storage : action * storage) : return = let main (action, storage : action * storage) : return =
match action with match action with

View File

@ -0,0 +1,167 @@
type id = int
type id_details = {
owner: address,
controller: address,
profile: bytes,
}
type buy = {
profile: bytes,
initial_controller: option(address),
}
type update_owner = {
id: id,
new_owner: address,
}
type update_details = {
id: id,
new_profile: option(bytes),
new_controller: option(address),
}
type action =
| Buy(buy)
| Update_owner(update_owner)
| Update_details(update_details)
| Skip(unit)
/* The prices kept in storage can be changed by bakers, though they should only be
adjusted down over time, not up. */
type storage = {
identities: big_map (id, id_details),
next_id: int,
name_price: tez,
skip_price: tez,
}
/** Preliminary thoughts on ids:
I very much like the simplicity of http://gurno.com/adam/mne/.
5 three letter words means you have a 15 character identity, not actually more
annoying than an IP address and a lot more memorable than the raw digits. This
can be stored as a single integer which is then translated into the corresponding
series of 5 words.
I in general like the idea of having a 'skip' mechanism, but it does need to cost
something so people don't eat up the address space. 256 ^ 5 means you have a lot
of address space, but if people troll by skipping a lot that could be eaten up.
Should probably do some napkin calculations for how expensive skipping needs to
be to deter people from doing it just to chew up address space.
*/
let buy = ((parameter, storage): (buy, storage)) : (list(operation), storage) => {
let void: unit =
if (amount == storage.name_price) { (); }
else { failwith("Incorrect amount paid."); };
let profile = parameter.profile;
let initial_controller = parameter.initial_controller;
let identities = storage.identities;
let new_id = storage.next_id;
let controller: address =
switch (initial_controller) {
| Some(addr) => addr
| None => sender
};
let new_id_details: id_details = {
owner : sender,
controller : controller,
profile : profile,
};
let updated_identities: big_map (id, id_details) =
Big_map.update(new_id, Some(new_id_details), identities);
(([]: list(operation)), { ...storage,
identities : updated_identities,
next_id : new_id + 1,
});
};
let update_owner = ((parameter, storage): (update_owner, storage)) : (list(operation), storage) => {
let void: unit =
if (amount != 0mutez) {
failwith("Updating owner doesn't cost anything.");
}
else { (); };
let id : int = parameter.id;
let new_owner = parameter.new_owner;
let identities = storage.identities;
let current_id_details: id_details =
switch (Big_map.find_opt(id, identities)) {
| Some(id_details) => id_details
| None => (failwith("This ID does not exist."): id_details)
};
let u: unit =
if (sender == current_id_details.owner) { (); }
else { failwith("You are not the owner of this ID."); };
let updated_id_details: id_details = {
owner : new_owner,
controller : current_id_details.controller,
profile : current_id_details.profile,
};
let updated_identities = Big_map.update(id, (Some updated_id_details), identities);
(([]: list(operation)), { ...storage, identities : updated_identities });
};
let update_details = ((parameter, storage): (update_details, storage)) :
(list(operation), storage) => {
let void : unit =
if (amount != 0mutez) {
failwith("Updating details doesn't cost anything.");
}
else { (); };
let id = parameter.id;
let new_profile = parameter.new_profile;
let new_controller = parameter.new_controller;
let identities = storage.identities;
let current_id_details: id_details =
switch (Big_map.find_opt(id, identities)) {
| Some(id_details) => id_details
| None => (failwith("This ID does not exist."): id_details)
};
let u: unit =
if ((sender != current_id_details.controller) &&
(sender != current_id_details.owner)) {
failwith ("You are not the owner or controller of this ID.")
}
else { (); };
let owner: address = current_id_details.owner;
let profile: bytes =
switch (new_profile) {
| None => /* Default */ current_id_details.profile
| Some(new_profile) => new_profile
};
let controller: address =
switch (new_controller) {
| None => /* Default */ current_id_details.controller
| Some new_controller => new_controller
};
let updated_id_details: id_details = {
owner : owner,
controller : controller,
profile : profile,
};
let updated_identities: big_map (id, id_details) =
Big_map.update(id, (Some updated_id_details), identities);
(([]: list(operation)), { ...storage, identities : updated_identities });
};
/* Let someone skip the next identity so nobody has to take one that's undesirable */
let skip = ((p,storage): (unit, storage)) => {
let void : unit =
if (amount != storage.skip_price) {
failwith("Incorrect amount paid.");
}
else { (); };
(([]: list(operation)), { ...storage, next_id : storage.next_id + 1 });
};
let main = ((action, storage): (action, storage)) : (list(operation), storage) => {
switch (action) {
| Buy(b) => buy((b, storage))
| Update_owner(uo) => update_owner((uo, storage))
| Update_details ud => update_details((ud, storage))
| Skip s => skip(((), storage))
};
};

View File

@ -0,0 +1,243 @@
(*_*
name: ID Contract (CameLIGO)
language: cameligo
compile:
entrypoint: main
dryRun:
entrypoint: main
parameters: |
Buy (
{
profile=0x0501000000026869;
initial_controller=Some(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address))
}
)
storage: |
{
identities=Big_map.literal[
(1,
{owner=("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
controller=("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address);
profile=0x0501000000026869}
);
];
next_id=2;
name_price=0tez;
skip_price=333mutez
}
deploy:
entrypoint: main
storage: |
{
identities=Big_map.literal[
(1,
{owner=("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
controller=("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address);
profile=0x0501000000026869}
);
];
next_id=2;
name_price=10tez;
skip_price=333mutez
}
evaluateValue:
entrypoint: ""
evaluateFunction:
entrypoint: buy
parameters: |
{
profile=0x0501000000026869;
initial_controller=Some(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address))
},
{
identities=Big_map.literal[
(1,
{owner=("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
controller=("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address);
profile=0x0501000000026869}
);
];
next_id=2;
name_price=0tez;
skip_price=333mutez
}
*_*)
type id = int
type id_details = {
owner: address;
controller: address;
profile: bytes;
}
type buy = {
profile: bytes;
initial_controller: address option;
}
type update_owner = {
id: id;
new_owner: address;
}
type update_details = {
id: id;
new_profile: bytes option;
new_controller: address option;
}
type action =
| Buy of buy
| Update_owner of update_owner
| Update_details of update_details
| Skip of unit
(* The prices kept in storage can be changed by bakers, though they should only be
adjusted down over time, not up. *)
type storage = {
identities: (id, id_details) big_map;
next_id: int;
name_price: tez;
skip_price: tez;
}
(** Preliminary thoughts on ids:
I very much like the simplicity of http://gurno.com/adam/mne/
Five three letter words means you have a 15 character identity, not actually more
annoying than an IP address and a lot more memorable than the raw digits. This
can be stored as a single integer which is then translated into the corresponding
series of 5 words.
I, in general like the idea of having a 'skip' mechanism, but it does need to cost
something so people don't eat up the address space. 256 ^ 5 means you have a lot
of address space, but if people troll by skipping a lot that could be eaten up.
Should probably do some napkin calculations for how expensive skipping needs to
be to deter people from doing it just to chew up address space.
*)
let buy (parameter, storage: buy * storage) =
let void: unit =
if amount = storage.name_price
then ()
else (failwith "Incorrect amount paid.": unit)
in
let profile = parameter.profile in
let initial_controller = parameter.initial_controller in
let identities = storage.identities in
let new_id = storage.next_id in
let controller: address =
match initial_controller with
| Some addr -> addr
| None -> sender
in
let new_id_details: id_details = {
owner = sender ;
controller = controller ;
profile = profile ;
}
in
let updated_identities: (id, id_details) big_map =
Big_map.update new_id (Some new_id_details) identities
in
([]: operation list), {identities = updated_identities;
next_id = new_id + 1;
name_price = storage.name_price;
skip_price = storage.skip_price;
}
let update_owner (parameter, storage: update_owner * storage) =
if (amount <> 0mutez)
then (failwith "Updating owner doesn't cost anything.": (operation list) * storage)
else
let id = parameter.id in
let new_owner = parameter.new_owner in
let identities = storage.identities in
let current_id_details: id_details =
match Big_map.find_opt id identities with
| Some id_details -> id_details
| None -> (failwith "This ID does not exist.": id_details)
in
let is_allowed: bool =
if sender = current_id_details.owner
then true
else (failwith "You are not the owner of this ID.": bool)
in
let updated_id_details: id_details = {
owner = new_owner;
controller = current_id_details.controller;
profile = current_id_details.profile;
}
in
let updated_identities = Big_map.update id (Some updated_id_details) identities in
([]: operation list), {identities = updated_identities;
next_id = storage.next_id;
name_price = storage.name_price;
skip_price = storage.skip_price;
}
let update_details (parameter, storage: update_details * storage) =
if (amount <> 0mutez)
then (failwith "Updating details doesn't cost anything.": (operation list) * storage)
else
let id = parameter.id in
let new_profile = parameter.new_profile in
let new_controller = parameter.new_controller in
let identities = storage.identities in
let current_id_details: id_details =
match Big_map.find_opt id identities with
| Some id_details -> id_details
| None -> (failwith "This ID does not exist.": id_details)
in
let is_allowed: bool =
if (sender = current_id_details.controller) || (sender = current_id_details.owner)
then true
else (failwith ("You are not the owner or controller of this ID."): bool)
in
let owner: address = current_id_details.owner in
let profile: bytes =
match new_profile with
| None -> (* Default *) current_id_details.profile
| Some new_profile -> new_profile
in
let controller: address =
match new_controller with
| None -> (* Default *) current_id_details.controller
| Some new_controller -> new_controller
in
let updated_id_details: id_details = {
owner = owner;
controller = controller;
profile = profile;
}
in
let updated_identities: (id, id_details) big_map =
Big_map.update id (Some updated_id_details) identities in
([]: operation list), {identities = updated_identities;
next_id = storage.next_id;
name_price = storage.name_price;
skip_price = storage.skip_price;
}
(* Let someone skip the next identity so nobody has to take one that's undesirable *)
let skip (p,storage: unit * storage) =
let void: unit =
if amount = storage.skip_price
then ()
else (failwith "Incorrect amount paid.": unit)
in
([]: operation list), {identities = storage.identities;
next_id = storage.next_id + 1;
name_price = storage.name_price;
skip_price = storage.skip_price;
}
let main (action, storage: action * storage) : operation list * storage =
match action with
| Buy b -> buy (b, storage)
| Update_owner uo -> update_owner (uo, storage)
| Update_details ud -> update_details (ud, storage)
| Skip s -> skip ((), storage)

View File

@ -0,0 +1,242 @@
(*_*
name: ID Contract (PascaLIGO)
language: pascaligo
compile:
entrypoint: main
dryRun:
entrypoint: main
parameters: |
Buy (
record [
profile=0x0501000000026869;
initial_controller=Some(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address))
]
)
storage: |
record [
identities=big_map[
1->record
[owner=("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
controller=("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address);
profile=0x0501000000026869]
];
next_id=2;
name_price=0tez;
skip_price=50mutez;
]
deploy:
entrypoint: main
storage: |
record [
identities=big_map[
1->record
[owner=("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
controller=("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address);
profile=0x0501000000026869]
];
next_id=2;
name_price=0tez;
skip_price=50mutez;
]
evaluateValue:
entrypoint: ""
evaluateFunction:
entrypoint: buy
parameters: |
(
record [
profile=0x0501000000026869;
initial_controller=Some(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address))
],
record [ identities=big_map[
1->record
[owner=("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
controller=("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address);
profile=0x0501000000026869]
];
next_id=2;
name_price=0tez;
skip_price=333mutez;
]
)
*_*)
type id is int
type id_details is
record [
owner: address;
controller: address;
profile: bytes;
]
type buy is
record [
profile: bytes;
initial_controller: option(address);
]
type update_owner is
record [
id: id;
new_owner: address;
]
type update_details is
record [
id: id;
new_profile: option(bytes);
new_controller: option(address);
]
type action is
| Buy of buy
| Update_owner of update_owner
| Update_details of update_details
| Skip of unit
(* The prices kept in storage can be changed by bakers, though they should only be
adjusted down over time, not up. *)
type storage is
record [
identities: big_map (id, id_details);
next_id: int;
name_price: tez;
skip_price: tez;
]
(** Preliminary thoughts on ids:
I very much like the simplicity of http://gurno.com/adam/mne/.
5 three letter words means you have a 15 character identity, not actually more
annoying than an IP address and a lot more memorable than the raw digits. This
can be stored as a single integer which is then translated into the corresponding
series of 5 words.
I in general like the idea of having a 'skip' mechanism, but it does need to cost
something so people don't eat up the address space. 256 ^ 5 means you have a lot
of address space, but if people troll by skipping a lot that could be eaten up.
Should probably do some napkin calculations for how expensive skipping needs to
be to deter people from doing it just to chew up address space.
*)
function buy (const parameter : buy; const storage : storage) : list(operation) * storage is
begin
if amount = storage.name_price
then skip
else failwith("Incorrect amount paid.");
const profile : bytes = parameter.profile;
const initial_controller : option(address) = parameter.initial_controller;
var identities : big_map (id, id_details) := storage.identities;
const new_id : int = storage.next_id;
const controller : address =
case initial_controller of
Some(addr) -> addr
| None -> sender
end;
const new_id_details: id_details =
record [
owner = sender ;
controller = controller ;
profile = profile ;
];
identities[new_id] := new_id_details;
end with ((nil : list(operation)), record [
identities = identities;
next_id = new_id + 1;
name_price = storage.name_price;
skip_price = storage.skip_price;
])
function update_owner (const parameter : update_owner; const storage : storage) :
list(operation) * storage is
begin
if (amount =/= 0mutez)
then
begin
failwith("Updating owner doesn't cost anything.");
end
else skip;
const id : int = parameter.id;
const new_owner : address = parameter.new_owner;
var identities : big_map (id, id_details) := storage.identities;
const id_details : id_details =
case identities[id] of
Some(id_details) -> id_details
| None -> (failwith("This ID does not exist."): id_details)
end;
var is_allowed : bool := False;
if sender = id_details.owner
then is_allowed := True
else failwith("You are not the owner of this ID.");
id_details.owner := new_owner;
identities[id] := id_details;
end with ((nil: list(operation)), record [
identities = identities;
next_id = storage.next_id;
name_price = storage.name_price;
skip_price = storage.skip_price;
])
function update_details (const parameter : update_details; const storage : storage ) :
list(operation) * storage is
begin
if (amount =/= 0mutez)
then failwith("Updating details doesn't cost anything.")
else skip;
const id : int = parameter.id;
const new_profile : option(bytes) = parameter.new_profile;
const new_controller : option(address) = parameter.new_controller;
const identities : big_map (id, id_details) = storage.identities;
const id_details: id_details =
case identities[id] of
Some(id_details) -> id_details
| None -> (failwith("This ID does not exist."): id_details)
end;
var is_allowed : bool := False;
if (sender = id_details.controller) or (sender = id_details.owner)
then is_allowed := True
else failwith("You are not the owner or controller of this ID.");
const owner: address = id_details.owner;
const profile: bytes =
case new_profile of
None -> (* Default *) id_details.profile
| Some(new_profile) -> new_profile
end;
const controller: address =
case new_controller of
None -> (* Default *) id_details.controller
| Some(new_controller) -> new_controller
end;
id_details.owner := owner;
id_details.controller := controller;
id_details.profile := profile;
identities[id] := id_details;
end with ((nil: list(operation)), record [
identities = identities;
next_id = storage.next_id;
name_price = storage.name_price;
skip_price = storage.skip_price;
])
(* Let someone skip the next identity so nobody has to take one that's undesirable *)
function skip_ (const p: unit; const storage: storage) : list(operation) * storage is
begin
if amount = storage.skip_price
then skip
else failwith("Incorrect amount paid.");
end with ((nil: list(operation)), record [
identities = storage.identities;
next_id = storage.next_id + 1;
name_price = storage.name_price;
skip_price = storage.skip_price;
])
function main (const action : action; const storage : storage) : list(operation) * storage is
case action of
| Buy(b) -> buy (b, storage)
| Update_owner(uo) -> update_owner (uo, storage)
| Update_details(ud) -> update_details (ud, storage)
| Skip(s) -> skip_ (unit, storage)
end;

View File

@ -0,0 +1,248 @@
/* (*_*
name: ID Contract (ReasonLIGO)
language: reasonligo
compile:
entrypoint: main
dryRun:
entrypoint: main
parameters: |
Buy (
{
profile: 0x0501000000026869,
initial_controller: Some(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address))
}
)
storage: |
{
identities:Big_map.literal([
(1,
{owner:("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address),
controller:("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), profile:0x0501000000026869}
)
]),
next_id:2,
name_price:0tez,
skip_price:333mutez
}
deploy:
entrypoint: main
storage: |
{
identities:Big_map.literal([
(1,
{owner:("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), controller:("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), profile:0x0501000000026869}
)
]),
next_id:2,
name_price:10tez,
skip_price:333mutez
}
evaluateValue:
entrypoint: ""
evaluateFunction:
entrypoint: buy
parameters: |
(
{
profile: 0x0501000000026869,
initial_controller: Some(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address))
},
{
identities:Big_map.literal([
(1,
{owner:("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address),
controller:("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address),
profile:0x0501000000026869}
)
]),
next_id:2,
name_price:0tez,
skip_price:333mutez
}
)
*_*) */
type id = int
type id_details = {
owner: address,
controller: address,
profile: bytes,
}
type buy = {
profile: bytes,
initial_controller: option(address),
}
type update_owner = {
id: id,
new_owner: address,
}
type update_details = {
id: id,
new_profile: option(bytes),
new_controller: option(address),
}
type action =
| Buy(buy)
| Update_owner(update_owner)
| Update_details(update_details)
| Skip(unit)
/* The prices kept in storage can be changed by bakers, though they should only be
adjusted down over time, not up. */
type storage = {
identities: big_map (id, id_details),
next_id: int,
name_price: tez,
skip_price: tez,
}
/** Preliminary thoughts on ids:
I very much like the simplicity of http://gurno.com/adam/mne/.
5 three letter words means you have a 15 character identity, not actually more
annoying than an IP address and a lot more memorable than the raw digits. This
can be stored as a single integer which is then translated into the corresponding
series of 5 words.
I in general like the idea of having a 'skip' mechanism, but it does need to cost
something so people don't eat up the address space. 256 ^ 5 means you have a lot
of address space, but if people troll by skipping a lot that could be eaten up.
Should probably do some napkin calculations for how expensive skipping needs to
be to deter people from doing it just to chew up address space.
*/
let buy = ((parameter, storage): (buy, storage)) : (list(operation), storage) => {
let void: unit =
if (amount == storage.name_price) { (); }
else { failwith("Incorrect amount paid."); };
let profile = parameter.profile;
let initial_controller = parameter.initial_controller;
let identities = storage.identities;
let new_id = storage.next_id;
let controller: address =
switch (initial_controller) {
| Some(addr) => addr
| None => sender
};
let new_id_details: id_details = {
owner : sender,
controller : controller,
profile : profile,
};
let updated_identities: big_map (id, id_details) =
Big_map.update(new_id, Some(new_id_details), identities);
(([]: list(operation)), {
identities : updated_identities,
next_id : new_id + 1,
name_price : storage.name_price,
skip_price : storage.skip_price,
});
};
let update_owner = ((parameter, storage): (update_owner, storage)) : (list(operation), storage) => {
let void: unit =
if (amount != 0mutez) {
failwith("Updating owner doesn't cost anything.");
}
else { (); };
let id : int = parameter.id;
let new_owner = parameter.new_owner;
let identities = storage.identities;
let current_id_details: id_details =
switch (Big_map.find_opt(id, identities)) {
| Some(id_details) => id_details
| None => (failwith("This ID does not exist."): id_details)
};
let is_allowed: bool =
if (sender == current_id_details.owner) { true; }
else { (failwith("You are not the owner of this ID."): bool); };
let updated_id_details: id_details = {
owner : new_owner,
controller : current_id_details.controller,
profile : current_id_details.profile,
};
let updated_identities = Big_map.update(id, (Some updated_id_details), identities);
(([]: list(operation)), {
identities : updated_identities,
next_id : storage.next_id,
name_price : storage.name_price,
skip_price : storage.skip_price,
});
};
let update_details = ((parameter, storage): (update_details, storage)) :
(list(operation), storage) => {
let void : unit =
if (amount != 0mutez) {
failwith("Updating details doesn't cost anything.");
}
else { (); };
let id = parameter.id;
let new_profile = parameter.new_profile;
let new_controller = parameter.new_controller;
let identities = storage.identities;
let current_id_details: id_details =
switch (Big_map.find_opt(id, identities)) {
| Some(id_details) => id_details
| None => (failwith("This ID does not exist."): id_details)
};
let is_allowed: bool =
if ((sender != current_id_details.controller) &&
(sender != current_id_details.owner)) {
(failwith ("You are not the owner or controller of this ID."): bool)
}
else { true; };
let owner: address = current_id_details.owner;
let profile: bytes =
switch (new_profile) {
| None => /* Default */ current_id_details.profile
| Some(new_profile) => new_profile
};
let controller: address =
switch (new_controller) {
| None => /* Default */ current_id_details.controller
| Some new_controller => new_controller
};
let updated_id_details: id_details = {
owner : owner,
controller : controller,
profile : profile,
};
let updated_identities: big_map (id, id_details) =
Big_map.update(id, (Some updated_id_details), identities);
(([]: list(operation)), {
identities : updated_identities,
next_id : storage.next_id,
name_price : storage.name_price,
skip_price : storage.skip_price,
});
};
/* Let someone skip the next identity so nobody has to take one that's undesirable */
let skip = ((p,storage): (unit, storage)) => {
let void : unit =
if (amount != storage.skip_price) {
failwith("Incorrect amount paid.");
}
else { (); };
(([]: list(operation)), {
identities : storage.identities,
next_id : storage.next_id + 1,
name_price : storage.name_price,
skip_price : storage.skip_price,
});
};
let main = ((action, storage): (action, storage)) : (list(operation), storage) => {
switch (action) {
| Buy(b) => buy((b, storage))
| Update_owner(uo) => update_owner((uo, storage))
| Update_details ud => update_details((ud, storage))
| Skip s => skip(((), storage))
};
};

View File

@ -40,9 +40,10 @@ let buy_id () =
("controller", e_address owner_addr) ; ("controller", e_address owner_addr) ;
("profile", owner_website)] ("profile", owner_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1)]) ; let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
e_int 1; ("next_id", e_int 1) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let new_addr = first_owner in let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options let options = Proto_alpha_utils.Memory_proto_alpha.make_options
@ -54,11 +55,15 @@ let buy_id () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let param = e_pair owner_website (e_some (e_address new_addr)) in let param = e_record_ez [("profile", owner_website) ;
let new_storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; ("initial_controller", (e_some (e_address new_addr))) ;
(e_int 1, id_details_2)]) ; ] in
e_int 2; let new_storage = e_record_ez [("identities", (e_big_map
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] [(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let%bind () = expect_eq ~options (program, state) "buy" let%bind () = expect_eq ~options (program, state) "buy"
(e_pair param storage) (e_pair param storage)
@ -73,9 +78,10 @@ let buy_id_sender_addr () =
("controller", e_address owner_addr) ; ("controller", e_address owner_addr) ;
("profile", owner_website)] ("profile", owner_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1)]) ; let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
e_int 1; ("next_id", e_int 1) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let new_addr = first_owner in let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options let options = Proto_alpha_utils.Memory_proto_alpha.make_options
@ -87,11 +93,14 @@ let buy_id_sender_addr () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let param = e_pair owner_website (e_typed_none (t_address ())) in let param = e_record_ez [("profile", owner_website) ;
let new_storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; ("initial_controller", (e_typed_none (t_address ())))] in
(e_int 1, id_details_2)]) ; let new_storage = e_record_ez [("identities", (e_big_map
e_int 2; [(e_int 0, id_details_1) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] (e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let%bind () = expect_eq ~options (program, state) "buy" let%bind () = expect_eq ~options (program, state) "buy"
(e_pair param storage) (e_pair param storage)
@ -107,18 +116,20 @@ let buy_id_wrong_amount () =
("controller", e_address owner_addr) ; ("controller", e_address owner_addr) ;
("profile", owner_website)] ("profile", owner_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1)]) ; let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
e_int 1; ("next_id", e_int 1) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let new_addr = first_owner in let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract ~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.fifty_cents) () ~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.fifty_cents) ()
in in
let param = e_pair owner_website (e_some (e_address new_addr)) in let param = e_record_ez [("profile", owner_website) ;
("initial_controller", (e_some (e_address new_addr)))] in
let%bind () = expect_string_failwith ~options (program, state) "buy" let%bind () = expect_string_failwith ~options (program, state) "buy"
(e_pair param storage) (e_pair param storage)
"Incorrect amount paid." "Incorrect amount paid."
in ok () in ok ()
@ -133,7 +144,7 @@ let update_details_owner () =
let new_addr = first_owner in let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract ~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero) ~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
() ()
in in
let new_website = e_bytes_string "ligolang.org" in let new_website = e_bytes_string "ligolang.org" in
@ -144,20 +155,24 @@ let update_details_owner () =
let id_details_2_diff = e_record_ez [("owner", e_address new_addr) ; let id_details_2_diff = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] in ("profile", new_website)] in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let new_storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let new_storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2_diff)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2_diff)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let details = e_bytes_string "ligolang.org" in let details = e_bytes_string "ligolang.org" in
let param = e_tuple [e_int 1 ; let param = e_record_ez [("id", e_int 1) ;
e_some details ; ("new_profile", e_some details) ;
e_some (e_address new_addr)] in ("new_controller", e_some (e_address new_addr))] in
let%bind () = expect_eq ~options (program, state) "update_details" let%bind () = expect_eq ~options (program, state) "update_details"
(e_pair param storage) (e_pair param storage)
(e_pair (e_list []) new_storage) (e_pair (e_list []) new_storage)
@ -185,20 +200,24 @@ let update_details_controller () =
let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ; let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ; ("controller", e_address owner_addr) ;
("profile", new_website)] in ("profile", new_website)] in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let new_storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let new_storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2_diff)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2_diff)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let details = e_bytes_string "ligolang.org" in let details = e_bytes_string "ligolang.org" in
let param = e_tuple [e_int 1 ; let param = e_record_ez [("id", e_int 1) ;
e_some details ; ("new_profile", e_some details) ;
e_some (e_address owner_addr)] in ("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_eq ~options (program, state) "update_details" let%bind () = expect_eq ~options (program, state) "update_details"
(e_pair param storage) (e_pair param storage)
(e_pair (e_list []) new_storage) (e_pair (e_list []) new_storage)
@ -224,15 +243,17 @@ let update_details_nonexistent () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let details = e_bytes_string "ligolang.org" in let details = e_bytes_string "ligolang.org" in
let param = e_tuple [e_int 2 ; let param = e_record_ez [("id", e_int 2) ;
e_some details ; ("new_profile", e_some details) ;
e_some (e_address owner_addr)] in ("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_string_failwith ~options (program, state) "update_details" let%bind () = expect_string_failwith ~options (program, state) "update_details"
(e_pair param storage) (e_pair param storage)
"This ID does not exist." "This ID does not exist."
@ -257,15 +278,17 @@ let update_details_wrong_addr () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let details = e_bytes_string "ligolang.org" in let details = e_bytes_string "ligolang.org" in
let param = e_tuple [e_int 0 ; let param = e_record_ez [("id", e_int 0) ;
e_some details ; ("new_profile", e_some details) ;
e_some (e_address owner_addr)] in ("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_string_failwith ~options (program, state) "update_details" let%bind () = expect_string_failwith ~options (program, state) "update_details"
(e_pair param storage) (e_pair param storage)
"You are not the owner or controller of this ID." "You are not the owner or controller of this ID."
@ -291,14 +314,16 @@ let update_details_unchanged () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let param = e_tuple [e_int 1 ; let param = e_record_ez [("id", e_int 1) ;
e_typed_none (t_bytes ()) ; ("new_profile", e_typed_none (t_bytes ())) ;
e_typed_none (t_address ())] in ("new_controller", e_typed_none (t_address ()))] in
let%bind () = expect_eq ~options (program, state) "update_details" let%bind () = expect_eq ~options (program, state) "update_details"
(e_pair param storage) (e_pair param storage)
(e_pair (e_list []) storage) (e_pair (e_list []) storage)
@ -326,17 +351,22 @@ let update_owner () =
let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ; let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] in ("profile", new_website)] in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let new_storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let new_storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2_diff)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2_diff)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let param = e_pair (e_int 1) (e_address owner_addr) in let param = e_record_ez [("id", e_int 1) ;
("new_owner", e_address owner_addr)] in
let%bind () = expect_eq ~options (program, state) "update_owner" let%bind () = expect_eq ~options (program, state) "update_owner"
(e_pair param storage) (e_pair param storage)
(e_pair (e_list []) new_storage) (e_pair (e_list []) new_storage)
@ -362,12 +392,15 @@ let update_owner_nonexistent () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let param = e_pair (e_int 2) (e_address new_addr) in let param = e_record_ez [("id", e_int 2);
("new_owner", e_address new_addr)] in
let%bind () = expect_string_failwith ~options (program, state) "update_owner" let%bind () = expect_string_failwith ~options (program, state) "update_owner"
(e_pair param storage) (e_pair param storage)
"This ID does not exist." "This ID does not exist."
@ -393,12 +426,15 @@ let update_owner_wrong_addr () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let param = e_pair (e_int 0) (e_address new_addr) in let param = e_record_ez [("id", e_int 0);
("new_owner", e_address new_addr)] in
let%bind () = expect_string_failwith ~options (program, state) "update_owner" let%bind () = expect_string_failwith ~options (program, state) "update_owner"
(e_pair param storage) (e_pair param storage)
"You are not the owner of this ID." "You are not the owner of this ID."
@ -422,15 +458,19 @@ let skip () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let new_storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let new_storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 3; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 3) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let%bind () = expect_eq ~options (program, state) "skip" let%bind () = expect_eq ~options (program, state) "skip"
(e_pair (e_unit ()) storage) (e_pair (e_unit ()) storage)
@ -456,17 +496,19 @@ let skip_wrong_amount () =
("controller", e_address new_addr) ; ("controller", e_address new_addr) ;
("profile", new_website)] ("profile", new_website)]
in in
let storage = e_tuple [(e_big_map [(e_int 0, id_details_1) ; let storage = e_record_ez [("identities", (e_big_map
(e_int 1, id_details_2)]) ; [(e_int 0, id_details_1) ;
e_int 2; (e_int 1, id_details_2)])) ;
e_tuple [e_mutez 1000000 ; e_mutez 1000000]] ("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in in
let%bind () = expect_string_failwith ~options (program, state) "skip" let%bind () = expect_string_failwith ~options (program, state) "skip"
(e_pair (e_unit ()) storage) (e_pair (e_unit ()) storage)
"Incorrect amount paid." "Incorrect amount paid."
in ok () in ok ()
let main = test_suite "ID Layer" [ let main = test_suite "ID Layer (CameLIGO)" [
test "buy" buy_id ; test "buy" buy_id ;
test "buy (sender addr)" buy_id_sender_addr ; test "buy (sender addr)" buy_id_sender_addr ;
test "buy (wrong amount)" buy_id_wrong_amount ; test "buy (wrong amount)" buy_id_wrong_amount ;

522
src/test/id_tests_p.ml Normal file
View File

@ -0,0 +1,522 @@
open Trace
open Test_helpers
open Ast_imperative
let type_file f =
let%bind typed,state = Ligo.Compile.Utils.type_file f "pascaligo" (Contract "main") in
ok (typed,state)
let get_program =
let s = ref None in
fun () -> match !s with
| Some s -> ok s
| None -> (
let%bind program = type_file "./contracts/id.ligo" in
s := Some program ;
ok program
)
let compile_main () =
let%bind typed_prg,_ = get_program () in
let%bind mini_c_prg = Ligo.Compile.Of_typed.compile typed_prg in
let%bind michelson_prg = Ligo.Compile.Of_mini_c.aggregate_and_compile_contract mini_c_prg "main" in
let%bind (_contract: Tezos_utils.Michelson.michelson) =
(* fails if the given entry point is not a valid contract *)
Ligo.Compile.Of_michelson.build_contract michelson_prg in
ok ()
let (first_owner , first_contract) =
let open Proto_alpha_utils.Memory_proto_alpha in
let id = List.nth dummy_environment.identities 0 in
let kt = id.implicit_contract in
Protocol.Alpha_context.Contract.to_b58check kt , kt
let buy_id () =
let%bind program, state = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
("next_id", e_int 1) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.one) ()
in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", owner_website)]
in
let param = e_record_ez [("profile", owner_website) ;
("initial_controller", (e_some (e_address new_addr))) ;
] in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_eq ~options (program, state) "buy"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
let buy_id_sender_addr () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
("next_id", e_int 1) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.one) ()
in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", owner_website)]
in
let param = e_record_ez [("profile", owner_website) ;
("initial_controller", (e_typed_none (t_address ())))] in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_eq ~options program "buy"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails if we attempt to buy an ID for the wrong amount *)
let buy_id_wrong_amount () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
("next_id", e_int 1) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.fifty_cents) ()
in
let param = e_record_ez [("profile", owner_website) ;
("initial_controller", (e_some (e_address new_addr)))] in
let%bind () = expect_string_failwith ~options program "buy"
(e_pair param storage)
"Incorrect amount paid."
in ok ()
let update_details_owner () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let id_details_2_diff = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", owner_website)] in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2_diff)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = owner_website in
let param = e_record_ez [("id", e_int 1) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address new_addr))] in
let%bind () = expect_eq ~options program "update_details"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
let update_details_controller () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = owner_website in
let id_details_2 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", new_website)] in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2_diff)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = e_bytes_string "ligolang.org" in
let param = e_record_ez [("id", e_int 1) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_eq ~options program "update_details"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails when we attempt to update details of nonexistent ID *)
let update_details_nonexistent () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = e_bytes_string "ligolang.org" in
let param = e_record_ez [("id", e_int 2) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_string_failwith ~options program "update_details"
(e_pair param storage)
"This ID does not exist."
in ok ()
(* Test that contract fails when we attempt to update details from wrong addr *)
let update_details_wrong_addr () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = e_bytes_string "ligolang.org" in
let param = e_record_ez [("id", e_int 0) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_string_failwith ~options program "update_details"
(e_pair param storage)
"You are not the owner or controller of this ID."
in ok ()
(* Test that giving none on both profile and controller address is a no-op *)
let update_details_unchanged () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 1) ;
("new_profile", e_typed_none (t_bytes ())) ;
("new_controller", e_typed_none (t_address ()))] in
let%bind () = expect_eq ~options program "update_details"
(e_pair param storage)
(e_pair (e_list []) storage)
in ok ()
let update_owner () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)] in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2_diff)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 1) ;
("new_owner", e_address owner_addr)] in
let%bind () = expect_eq ~options program "update_owner"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails when we attempt to update owner of nonexistent ID *)
let update_owner_nonexistent () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 2);
("new_owner", e_address new_addr)] in
let%bind () = expect_string_failwith ~options program "update_owner"
(e_pair param storage)
"This ID does not exist."
in ok ()
(* Test that contract fails when we attempt to update owner from non-owner addr *)
let update_owner_wrong_addr () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 0);
("new_owner", e_address new_addr)] in
let%bind () = expect_string_failwith ~options program "update_owner"
(e_pair param storage)
"You are not the owner of this ID."
in ok ()
let skip () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.one) ()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 3) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_eq ~options program "skip_"
(e_pair (e_unit ()) storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails if we try to skip without paying the right amount *)
let skip_wrong_amount () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.fifty_cents) ()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_string_failwith ~options program "skip_"
(e_pair (e_unit ()) storage)
"Incorrect amount paid."
in ok ()
let main = test_suite "ID Layer (PascaLIGO)" [
test "buy" buy_id ;
test "buy (sender addr)" buy_id_sender_addr ;
test "buy (wrong amount)" buy_id_wrong_amount ;
test "update_details (owner)" update_details_owner ;
test "update_details (controller)" update_details_controller ;
test "update_details_nonexistent" update_details_nonexistent ;
test "update_details_wrong_addr" update_details_wrong_addr ;
test "update_details_unchanged" update_details_unchanged ;
test "update_owner" update_owner ;
test "update_owner_nonexistent" update_owner_nonexistent ;
test "update_owner_wrong_addr" update_owner_wrong_addr ;
test "skip" skip ;
test "skip (wrong amount)" skip_wrong_amount ;
]

525
src/test/id_tests_r.ml Normal file
View File

@ -0,0 +1,525 @@
open Trace
open Test_helpers
open Ast_imperative
let retype_file f =
let%bind typed,state = Ligo.Compile.Utils.type_file f "reasonligo" (Contract "main") in
ok (typed,state)
let get_program =
let s = ref None in
fun () -> match !s with
| Some s -> ok s
| None -> (
let%bind program = retype_file "./contracts/id.religo" in
s := Some program ;
ok program
)
let compile_main () =
let%bind typed_prg,_ = get_program () in
let%bind mini_c_prg = Ligo.Compile.Of_typed.compile typed_prg in
let%bind michelson_prg = Ligo.Compile.Of_mini_c.aggregate_and_compile_contract mini_c_prg "main" in
let%bind (_contract: Tezos_utils.Michelson.michelson) =
(* fails if the given entry point is not a valid contract *)
Ligo.Compile.Of_michelson.build_contract michelson_prg in
ok ()
let (first_owner , first_contract) =
let open Proto_alpha_utils.Memory_proto_alpha in
let id = List.nth dummy_environment.identities 0 in
let kt = id.implicit_contract in
Protocol.Alpha_context.Contract.to_b58check kt , kt
let buy_id () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
("next_id", e_int 1) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.one) ()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let param = e_record_ez [("profile", owner_website) ;
("initial_controller", (e_some (e_address new_addr))) ;
] in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_eq ~options program "buy"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
let buy_id_sender_addr () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
("next_id", e_int 1) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.one) ()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let param = e_record_ez [("profile", owner_website) ;
("initial_controller", (e_typed_none (t_address ())))] in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_eq ~options program "buy"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails if we attempt to buy an ID for the wrong amount *)
let buy_id_wrong_amount () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let storage = e_record_ez [("identities", (e_big_map [(e_int 0, id_details_1)])) ;
("next_id", e_int 1) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.fifty_cents) ()
in
let param = e_record_ez [("profile", owner_website) ;
("initial_controller", (e_some (e_address new_addr)))] in
let%bind () = expect_string_failwith ~options program "buy"
(e_pair param storage)
"Incorrect amount paid."
in ok ()
let update_details_owner () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address owner_addr) ;
("profile", new_website)]
in
let id_details_2_diff = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)] in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2_diff)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = e_bytes_string "ligolang.org" in
let param = e_record_ez [("id", e_int 1) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address new_addr))] in
let%bind () = expect_eq ~options program "update_details"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
let update_details_controller () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", new_website)] in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2_diff)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = e_bytes_string "ligolang.org" in
let param = e_record_ez [("id", e_int 1) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_eq ~options program "update_details"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails when we attempt to update details of nonexistent ID *)
let update_details_nonexistent () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = e_bytes_string "ligolang.org" in
let param = e_record_ez [("id", e_int 2) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_string_failwith ~options program "update_details"
(e_pair param storage)
"This ID does not exist."
in ok ()
(* Test that contract fails when we attempt to update details from wrong addr *)
let update_details_wrong_addr () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let details = e_bytes_string "ligolang.org" in
let param = e_record_ez [("id", e_int 0) ;
("new_profile", e_some details) ;
("new_controller", e_some (e_address owner_addr))] in
let%bind () = expect_string_failwith ~options program "update_details"
(e_pair param storage)
"You are not the owner or controller of this ID."
in ok ()
(* Test that giving none on both profile and controller address is a no-op *)
let update_details_unchanged () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 1) ;
("new_profile", e_typed_none (t_bytes ())) ;
("new_controller", e_typed_none (t_address ()))] in
let%bind () = expect_eq ~options program "update_details"
(e_pair param storage)
(e_pair (e_list []) storage)
in ok ()
let update_owner () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let id_details_2_diff = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)] in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2_diff)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 1) ;
("new_owner", e_address owner_addr)] in
let%bind () = expect_eq ~options program "update_owner"
(e_pair param storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails when we attempt to update owner of nonexistent ID *)
let update_owner_nonexistent () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 2);
("new_owner", e_address new_addr)] in
let%bind () = expect_string_failwith ~options program "update_owner"
(e_pair param storage)
"This ID does not exist."
in ok ()
(* Test that contract fails when we attempt to update owner from non-owner addr *)
let update_owner_wrong_addr () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.zero)
()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let param = e_record_ez [("id", e_int 0);
("new_owner", e_address new_addr)] in
let%bind () = expect_string_failwith ~options program "update_owner"
(e_pair param storage)
"You are not the owner of this ID."
in ok ()
let skip () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.one) ()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let new_storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 3) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_eq ~options program "skip"
(e_pair (e_unit ()) storage)
(e_pair (e_list []) new_storage)
in ok ()
(* Test that contract fails if we try to skip without paying the right amount *)
let skip_wrong_amount () =
let%bind program = get_program () in
let owner_addr = addr 5 in
let owner_website = e_bytes_string "ligolang.org" in
let id_details_1 = e_record_ez [("owner", e_address owner_addr) ;
("controller", e_address owner_addr) ;
("profile", owner_website)]
in
let new_addr = first_owner in
let options = Proto_alpha_utils.Memory_proto_alpha.make_options
~sender:first_contract
~amount:(Memory_proto_alpha.Protocol.Alpha_context.Tez.fifty_cents) ()
in
let new_website = e_bytes_string "ligolang.org" in
let id_details_2 = e_record_ez [("owner", e_address new_addr) ;
("controller", e_address new_addr) ;
("profile", new_website)]
in
let storage = e_record_ez [("identities", (e_big_map
[(e_int 0, id_details_1) ;
(e_int 1, id_details_2)])) ;
("next_id", e_int 2) ;
("name_price", e_mutez 1000000) ;
("skip_price", e_mutez 1000000) ; ]
in
let%bind () = expect_string_failwith ~options program "skip"
(e_pair (e_unit ()) storage)
"Incorrect amount paid."
in ok ()
let main = test_suite "ID Layer (ReasonLIGO)" [
test "buy" buy_id ;
test "buy (sender addr)" buy_id_sender_addr ;
test "buy (wrong amount)" buy_id_wrong_amount ;
test "update_details (owner)" update_details_owner ;
test "update_details (controller)" update_details_controller ;
test "update_details_nonexistent" update_details_nonexistent ;
test "update_details_wrong_addr" update_details_wrong_addr ;
test "update_details_unchanged" update_details_unchanged ;
test "update_owner" update_owner ;
test "update_owner_nonexistent" update_owner_nonexistent ;
test "update_owner_wrong_addr" update_owner_wrong_addr ;
test "skip" skip ;
test "skip (wrong amount)" skip_wrong_amount ;
]

View File

@ -11,6 +11,8 @@ let () =
Coase_tests.main ; Coase_tests.main ;
Vote_tests.main ; Vote_tests.main ;
Id_tests.main ; Id_tests.main ;
Id_tests_p.main ;
Id_tests_r.main ;
Multisig_tests.main ; Multisig_tests.main ;
Multisig_v2_tests.main ; Multisig_v2_tests.main ;
Replaceable_id_tests.main ; Replaceable_id_tests.main ;

View File

@ -4,12 +4,6 @@ const join = require('path').join;
const fs = require('fs'); const fs = require('fs');
const YAML = require('yamljs'); const YAML = require('yamljs');
const CURATED_EXAMPLES = [
'cameligo/arithmetic-contract.ligo',
'pascaligo/arithmetic-contract.ligo',
'reasonligo/arithmetic-contract.ligo'
];
function urlFriendlyHash(content) { function urlFriendlyHash(content) {
const hash = createHash('md5'); const hash = createHash('md5');
hash.update(content); hash.update(content);
@ -109,6 +103,15 @@ async function main() {
// const EXAMPLES_GLOB = '**/*.ligo'; // const EXAMPLES_GLOB = '**/*.ligo';
// const files = await findFiles(EXAMPLES_GLOB, EXAMPLES_DIR); // const files = await findFiles(EXAMPLES_GLOB, EXAMPLES_DIR);
const CURATED_EXAMPLES = [
'pascaligo/arithmetic-contract.ligo',
'cameligo/arithmetic-contract.ligo',
'reasonligo/arithmetic-contract.ligo',
'pascaligo/id.ligo',
'cameligo/id.mligo',
'reasonligo/id.religo',
];
const EXAMPLES_DEST_DIR = join(process.cwd(), 'build', 'static', 'examples'); const EXAMPLES_DEST_DIR = join(process.cwd(), 'build', 'static', 'examples');
fs.mkdirSync(EXAMPLES_DEST_DIR, { recursive: true }); fs.mkdirSync(EXAMPLES_DEST_DIR, { recursive: true });

View File

@ -17,7 +17,7 @@ export interface ExamplesState {
export class ChangeSelectedAction { export class ChangeSelectedAction {
public readonly type = ActionType.ChangeSelected; public readonly type = ActionType.ChangeSelected;
constructor(public payload: ExamplesState['selected']) {} constructor(public payload: ExamplesState['selected']) { }
} }
export class ClearSelectedAction { export class ClearSelectedAction {
@ -33,9 +33,12 @@ export const DEFAULT_STATE: ExamplesState = {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
DEFAULT_STATE.list = [ DEFAULT_STATE.list = [
{ id: 'MzkMQ1oiVHJqbcfUuVFKTw', name: 'CameLIGO Contract' }, { id: 'MzkMQ1oiVHJqbcfUuVFKTw', name: 'Increment Example CameLIGO ' },
{ id: 'FEb62HL7onjg1424eUsGSg', name: 'PascaLIGO Contract' }, { id: 'FEb62HL7onjg1424eUsGSg', name: 'Increment Example PascaLIGO' },
{ id: 'JPhSOehj_2MFwRIlml0ymQ', name: 'ReasonLIGO Contract' } { id: 'JPhSOehj_2MFwRIlml0ymQ', name: 'Increment Example ReasonLIGO' },
{ id: 'ehDv-Xaf70mQoiPhQDTAUQ', name: 'ID Example CameLIGO' },
{ id: 'CpnK7TFuUjJiQTT8KiiGyQ', name: 'ID Example ReasonLIGO' },
{ id: 'yP-THvmURsaqHxpwCravWg', name: 'ID Example PascaLIGO' },
]; ];
} }

View File

@ -24,7 +24,7 @@ describe('Share', () => {
await responseCallback; await responseCallback;
const actualShareLink = await page.evaluate(getInputValue, 'share-link'); const actualShareLink = await page.evaluate(getInputValue, 'share-link');
const expectedShareLink = `${API_HOST}/p/WxKPBq9-mkZ_kq4cMHXfCQ`; const expectedShareLink = `${API_HOST}/p/2GnQR0cUYeO7feAw71SJYQ`
expect(actualShareLink).toEqual(expectedShareLink); expect(actualShareLink).toEqual(expectedShareLink);
done(); done();