Alpha: classify and document contract errors.

This commit is contained in:
Benjamin Canou 2017-03-09 19:17:13 +01:00
parent d845dc9740
commit 9caef6fae4
21 changed files with 289 additions and 248 deletions

View File

@ -128,7 +128,7 @@ let originate_contract cctxt
~source ~src_pk ~src_sk ~manager_pkh ~balance ?delegatable ?delegatePubKey ~source ~src_pk ~src_sk ~manager_pkh ~balance ?delegatable ?delegatePubKey
~(code:Script.code) ~init ~fee () = ~(code:Script.code) ~init ~fee () =
Client_proto_programs.parse_data cctxt init >>= fun storage -> Client_proto_programs.parse_data cctxt init >>= fun storage ->
let init = Script.{ storage ; storage_type = code.storage_type } in let storage = Script.{ storage ; storage_type = code.storage_type } in
Client_proto_rpcs.Context.Contract.counter cctxt block source >>=? fun pcounter -> Client_proto_rpcs.Context.Contract.counter cctxt block source >>=? fun pcounter ->
let counter = Int32.succ pcounter in let counter = Int32.succ pcounter in
cctxt.message "Acquired the source's sequence counter (%ld -> %ld)." cctxt.message "Acquired the source's sequence counter (%ld -> %ld)."
@ -138,7 +138,7 @@ let originate_contract cctxt
~net ~source ~sourcePubKey:src_pk ~managerPubKey:manager_pkh ~net ~source ~sourcePubKey:src_pk ~managerPubKey:manager_pkh
~counter ~balance ~spendable:!spendable ~counter ~balance ~spendable:!spendable
?delegatable ?delegatePubKey ?delegatable ?delegatePubKey
~script:(code, init) ~fee () >>=? fun bytes -> ~script:{ code ; storage } ~fee () >>=? fun bytes ->
let signature = Ed25519.sign src_sk bytes in let signature = Ed25519.sign src_sk bytes in
originate cctxt ?force ~block ~signature bytes originate cctxt ?force ~block ~signature bytes

View File

@ -471,7 +471,7 @@ let parse_data_type cctxt s =
with with
| exn -> report_parse_error cctxt "data_type: " exn lexbuf | exn -> report_parse_error cctxt "data_type: " exn lexbuf
let unexpand_macros type_map program = let unexpand_macros type_map (program : Script.code) =
let open Script in let open Script in
let rec caddr type_map acc = function let rec caddr type_map acc = function
| [] -> Some (List.rev acc) | [] -> Some (List.rev acc)

View File

@ -108,7 +108,7 @@ module Context = struct
balance: Tez.t ; balance: Tez.t ;
spendable: bool ; spendable: bool ;
delegate: bool * public_key_hash option ; delegate: bool * public_key_hash option ;
script: Script.t ; script: Script.t option ;
assets: Asset.Map.t ; assets: Asset.Map.t ;
counter: int32 ; counter: int32 ;
} }
@ -180,10 +180,6 @@ module Helpers = struct
module Forge = struct module Forge = struct
let script_of_option = function
| None -> Script.No_script
| Some (code, storage) -> Script { code ; storage }
open Operation open Operation
module Manager = struct module Manager = struct
@ -206,7 +202,6 @@ module Helpers = struct
?(spendable = true) ?(spendable = true)
?(delegatable = true) ?(delegatable = true)
?delegatePubKey ?script ~fee () = ?delegatePubKey ?script ~fee () =
let script = script_of_option script in
operations cctxt block ~net ~source ?sourcePubKey ~counter ~fee operations cctxt block ~net ~source ?sourcePubKey ~counter ~fee
Tezos_context.[ Tezos_context.[
Origination { manager = managerPubKey ; Origination { manager = managerPubKey ;

View File

@ -91,7 +91,7 @@ module Context : sig
balance: Tez.t ; balance: Tez.t ;
spendable: bool ; spendable: bool ;
delegate: bool * public_key_hash option ; delegate: bool * public_key_hash option ;
script: Script.t ; script: Script.t option ;
assets: Asset.Map.t ; assets: Asset.Map.t ;
counter: int32 ; counter: int32 ;
} }
@ -124,7 +124,7 @@ module Context : sig
bool tzresult Lwt.t bool tzresult Lwt.t
val script: val script:
Client_commands.context -> Client_commands.context ->
block -> Contract.t -> Script.t tzresult Lwt.t block -> Contract.t -> Script.t option tzresult Lwt.t
val assets: val assets:
Client_commands.context -> Client_commands.context ->
block -> Contract.t -> block -> Contract.t ->
@ -222,7 +222,7 @@ module Helpers : sig
?spendable:bool -> ?spendable:bool ->
?delegatable:bool -> ?delegatable:bool ->
?delegatePubKey: public_key_hash -> ?delegatePubKey: public_key_hash ->
?script:(Script.code * Script.storage) -> ?script:Script.t ->
fee:Tez.t-> fee:Tez.t->
unit -> unit ->
MBytes.t tzresult Lwt.t MBytes.t tzresult Lwt.t

View File

@ -12,7 +12,6 @@
open Tezos_context open Tezos_context
type error += Bad_endorsement (* TODO: doc *) type error += Bad_endorsement (* TODO: doc *)
type error += Insert_coin (* TODO: doc *)
type error += Contract_not_delegatable (* TODO: doc *) type error += Contract_not_delegatable (* TODO: doc *)
type error += Unimplemented type error += Unimplemented
type error += Invalid_voting_period type error += Invalid_voting_period
@ -53,13 +52,13 @@ let apply_manager_operation_content ctxt origination_nonce accept_failing_script
Contract.spend ctxt source amount >>=? fun ctxt -> Contract.spend ctxt source amount >>=? fun ctxt ->
Contract.credit ctxt destination amount >>=? fun ctxt -> Contract.credit ctxt destination amount >>=? fun ctxt ->
Contract.get_script ctxt destination >>=? function Contract.get_script ctxt destination >>=? function
| No_script -> begin | None -> begin
match parameters with match parameters with
| None | Some (Prim (_, "Unit", [])) -> | None | Some (Prim (_, "Unit", [])) ->
return (ctxt, origination_nonce) return (ctxt, origination_nonce)
| Some _ -> fail Non_scripted_contract_with_parameter | Some _ -> fail Non_scripted_contract_with_parameter
end end
| Script { code ; storage } -> | Some { code ; storage } ->
match parameters with match parameters with
| None -> fail Scripted_contract_without_paramater | None -> fail Scripted_contract_without_paramater
| Some parameters -> | Some parameters ->
@ -71,8 +70,9 @@ let apply_manager_operation_content ctxt origination_nonce accept_failing_script
| Ok (storage_res, _res, _steps, ctxt, origination_nonce) -> | Ok (storage_res, _res, _steps, ctxt, origination_nonce) ->
(* TODO: pay for the steps and the storage diff: (* TODO: pay for the steps and the storage diff:
update_script_storage checks the storage cost *) update_script_storage checks the storage cost *)
Contract.update_script_storage Contract.update_script_storage_and_fees
ctxt destination storage_res >>=? fun ctxt -> ctxt destination
Script_interpreter.dummy_storage_fee storage_res >>=? fun ctxt ->
return (ctxt, origination_nonce) return (ctxt, origination_nonce)
| Error err -> | Error err ->
if accept_failing_script && is_reject err then if accept_failing_script && is_reject err then
@ -81,24 +81,18 @@ let apply_manager_operation_content ctxt origination_nonce accept_failing_script
Lwt.return (Error err) Lwt.return (Error err)
end end
| Origination { manager ; delegate ; script ; | Origination { manager ; delegate ; script ;
spendable ; delegatable ; credit } -> begin spendable ; delegatable ; credit } ->
match script with let script = match script with
| No_script -> return () | None -> None
| Script { code ; storage } -> | Some script ->
Script_ir_translator.parse_script ctxt storage code >>=? fun _ -> Some (script, (Script_interpreter.dummy_code_fee, Script_interpreter.dummy_storage_fee)) in
let storage_fee = Script.storage_cost storage in
let code_fee = Script.code_cost code in
Lwt.return Tez.(code_fee +? storage_fee) >>=? fun script_fee ->
Lwt.return Tez.(script_fee +? Constants.origination_burn) >>=? fun total_fee ->
fail_unless Tez.(credit > total_fee) Insert_coin >>=? fun () ->
return ()
end >>=? fun () ->
Contract.spend ctxt source credit >>=? fun ctxt -> Contract.spend ctxt source credit >>=? fun ctxt ->
Lwt.return Tez.(credit -? Constants.origination_burn) >>=? fun balance -> Lwt.return Tez.(credit -? Constants.origination_burn) >>=? fun balance ->
Contract.originate ctxt Contract.originate ctxt
origination_nonce origination_nonce
~manager ~delegate ~balance ~manager ~delegate ~balance
~script ~spendable ~delegatable >>=? fun (ctxt, _, origination_nonce) -> ?script
~spendable ~delegatable >>=? fun (ctxt, _, origination_nonce) ->
return (ctxt, origination_nonce) return (ctxt, origination_nonce)
| Issuance { asset = (asset, key); amount } -> | Issuance { asset = (asset, key); amount } ->
Contract.issue ctxt source asset key amount >>=? fun ctxt -> Contract.issue ctxt source asset key amount >>=? fun ctxt ->
@ -127,6 +121,7 @@ let apply_sourced_operation
operation origination_nonce ops = operation origination_nonce ops =
match ops with match ops with
| Manager_operations { source ; public_key ; fee ; counter ; operations = contents } -> | Manager_operations { source ; public_key ; fee ; counter ; operations = contents } ->
Contract.must_exist ctxt source >>=? fun () ->
Contract.get_manager ctxt source >>=? fun manager -> Contract.get_manager ctxt source >>=? fun manager ->
check_signature_and_update_public_key check_signature_and_update_public_key
ctxt manager public_key operation >>=? fun ctxt -> ctxt manager public_key operation >>=? fun ctxt ->
@ -139,6 +134,7 @@ let apply_sourced_operation
| Some contract -> | Some contract ->
Contract.credit ctxt contract fee) >>=? fun ctxt -> Contract.credit ctxt contract fee) >>=? fun ctxt ->
fold_left_s (fun (ctxt, origination_nonce) content -> fold_left_s (fun (ctxt, origination_nonce) content ->
Contract.must_exist ctxt source >>=? fun () ->
apply_manager_operation_content ctxt origination_nonce apply_manager_operation_content ctxt origination_nonce
accept_failing_script source content) accept_failing_script source content)
(ctxt, origination_nonce) contents (ctxt, origination_nonce) contents
@ -190,7 +186,7 @@ let apply_anonymous_operation ctxt miner_contract origination_nonce kind =
end >>=? fun delegate -> end >>=? fun delegate ->
Contract.originate ctxt Contract.originate ctxt
origination_nonce origination_nonce
~manager ~delegate ~balance:Constants.faucet_credit ~script:No_script ~manager ~delegate ~balance:Constants.faucet_credit ?script:None
~spendable:true ~delegatable:true >>=? fun (ctxt, _, origination_nonce) -> ~spendable:true ~delegatable:true >>=? fun (ctxt, _, origination_nonce) ->
return (ctxt, origination_nonce) return (ctxt, origination_nonce)

View File

@ -14,7 +14,7 @@ type t =
| Originated of Contract_hash.t | Originated of Contract_hash.t
type contract = t type contract = t
type error += Invalid_contract_notation of string type error += Invalid_contract_notation of string (* `Permanent *)
let to_b58check = function let to_b58check = function
| Default pbk -> Ed25519.Public_key_hash.to_b58check pbk | Default pbk -> Ed25519.Public_key_hash.to_b58check pbk
@ -26,17 +26,23 @@ let of_b58check s =
| Some (Contract_hash.Hash h) -> ok (Originated h) | Some (Contract_hash.Hash h) -> ok (Originated h)
| _ -> error (Invalid_contract_notation s) | _ -> error (Invalid_contract_notation s)
let pp ppf = function
| Default pbk -> Ed25519.Public_key_hash.pp ppf pbk
| Originated h -> Contract_hash.pp ppf h
let pp_short ppf = function
| Default pbk -> Ed25519.Public_key_hash.pp_short ppf pbk
| Originated h -> Contract_hash.pp_short ppf h
let encoding = let encoding =
let open Data_encoding in let open Data_encoding in
describe describe
~title: ~title:
"A contract handle" "A contract handle"
~description: ~description:
"A contract notation as given to a RPC or inside scripts. \ "A contract notation as given to an RPC or inside scripts. \
Contract handles can be written 'd<base64 encoded ID>d' \ Can be a base58 public key hash, representing the default contract \
for the default contract of some ID (public key hash) or \ of this identity, or a base58 originated contract hash." @@
'h<base64 encoded contract ID>h' for a created contract or account, \
as replied by the contract origination RPC." @@
splitted splitted
~binary: ~binary:
(union ~tag_size:`Uint8 [ (union ~tag_size:`Uint8 [
@ -60,14 +66,11 @@ let () =
let open Data_encoding in let open Data_encoding in
register_error_kind register_error_kind
`Permanent `Permanent
~id:"InvalidContractNotationError" ~id:"contract.invalid_contract_notation"
~title: "Invalid contract notation" ~title: "Invalid contract notation"
~pp: (fun ppf x -> Format.fprintf ppf "Invalid contract notation %S" x)
~description: ~description:
"A malformed contract notation was given to a RPC or by a script. \ "A malformed contract notation was given to an RPC or in a script."
Contract handles can be written 'd<base encoded ID>d' \
for the default contract of some ID (public key hash) or \
'h<base encoded contract ID>h' for a created contract or account, \
as replied by the contract origination RPC."
(obj1 (req "notation" string)) (obj1 (req "notation" string))
(function Invalid_contract_notation loc -> Some loc | _ -> None) (function Invalid_contract_notation loc -> Some loc | _ -> None)
(fun loc -> Invalid_contract_notation loc) (fun loc -> Invalid_contract_notation loc)
@ -78,7 +81,6 @@ let is_default = function
| Default m -> Some m | Default m -> Some m
| Originated _ -> None | Originated _ -> None
type origination_nonce = type origination_nonce =
{ operation_hash: Operation_hash.t ; { operation_hash: Operation_hash.t ;
origination_index: int32 } origination_index: int32 }

View File

@ -16,27 +16,42 @@ type contract = t
include Compare.S with type t := contract include Compare.S with type t := contract
(** {2 Default contracts} *****************************************************)
val default_contract : Ed25519.Public_key_hash.t -> contract val default_contract : Ed25519.Public_key_hash.t -> contract
val is_default : contract -> Ed25519.Public_key_hash.t option val is_default : contract -> Ed25519.Public_key_hash.t option
(** {2 Originated contracts} **************************************************)
(** Originated contracts handles are crafted from the hash of the
operation that triggered their origination (and nothing else).
As a single operation can trigger several originations, the
corresponding handles are forged from a deterministic sequence of
nonces, initialized with the hash of the operation. *)
type origination_nonce type origination_nonce
val originated_contract : origination_nonce -> contract val originated_contract : origination_nonce -> contract
val originated_contracts : origination_nonce -> contract list val originated_contracts : origination_nonce -> contract list
val initial_origination_nonce : Operation_hash.t -> origination_nonce val initial_origination_nonce : Operation_hash.t -> origination_nonce
val incr_origination_nonce : origination_nonce -> origination_nonce val incr_origination_nonce : origination_nonce -> origination_nonce
(** {2 Human readable notation} ***********************************************) (** {2 Human readable notation} ***********************************************)
type error += Invalid_contract_notation of string type error += Invalid_contract_notation of string (* `Permanent *)
val to_b58check: contract -> string val to_b58check: contract -> string
val of_b58check: string -> contract tzresult val of_b58check: string -> contract tzresult
val pp: Format.formatter -> contract -> unit
val pp_short: Format.formatter -> contract -> unit
(** {2 Serializers} ***********************************************************) (** {2 Serializers} ***********************************************************)
val encoding : contract Data_encoding.t val encoding : contract Data_encoding.t

View File

@ -8,64 +8,71 @@
(**************************************************************************) (**************************************************************************)
type error += type error +=
| Balance_too_low of Contract_repr.contract * Tez_repr.t * Tez_repr.t | Initial_amount_too_low of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Permanent *)
| Initial_amount_too_low of Tez_repr.t * Tez_repr.t | Balance_too_low of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Temporary *)
| Counter_in_the_past of Contract_repr.contract * int32 * int32 | Cannot_pay_storage_fee of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Temporary *)
| Counter_in_the_future of Contract_repr.contract * int32 * int32 | Counter_in_the_past of Contract_repr.contract * int32 * int32 (* `Branch *)
| Unspendable_contract of Contract_repr.contract | Counter_in_the_future of Contract_repr.contract * int32 * int32 (* `Temporary *)
| Non_existing_contract (* TODO: DOC *) | Unspendable_contract of Contract_repr.contract (* `Permanent *)
| No_delegate (* TODO: DOC *) | Non_existing_contract of Contract_repr.contract (* `Temporary *)
| Undelagatable_contract (* TODO: DOC *) | Undelagatable_contract of Contract_repr.contract (* `Permanent *)
| Failure of string | Failure of string (* `Permanent *)
let () = let () =
register_error_kind
`Permanent
~id:"contract.failure"
~title:"Contract storage failure"
~description:"Unexpected contract storage error"
~pp:(fun ppf s -> Format.fprintf ppf "Contract_storage.Failure %S" s)
Data_encoding.(obj1 (req "message" string))
(function Failure s -> Some s | _ -> None)
(fun s -> Failure s) ;
register_error_kind register_error_kind
`Permanent `Permanent
~id:"contract.initial_amount_too_low" ~id:"contract.initial_amount_too_low"
~title:"Initial amount too low" ~title:"Initial amount too low"
~description:"Not enough tokens provided for an origination" ~description:"Not enough tokens provided for an origination"
~pp:(fun ppf (r, p) -> ~pp:(fun ppf (c, r, p) ->
Format.fprintf ppf "Initial amount too low (required %a but provided %a)" Format.fprintf ppf "Initial amount of contract %a too low (required %a but provided %a)"
Contract_repr.pp c
Tez_repr.pp r Tez_repr.pp p) Tez_repr.pp r Tez_repr.pp p)
Data_encoding.(obj2 Data_encoding.(obj3
(req "contract" Contract_repr.encoding)
(req "required" Tez_repr.encoding) (req "required" Tez_repr.encoding)
(req "provided" Tez_repr.encoding)) (req "provided" Tez_repr.encoding))
(function Initial_amount_too_low (r, p) -> Some (r, p) | _ -> None) (function Initial_amount_too_low (c, r, p) -> Some (c, r, p) | _ -> None)
(fun (r, p) -> Initial_amount_too_low (r, p)) ; (fun (c, r, p) -> Initial_amount_too_low (c, r, p)) ;
register_error_kind register_error_kind
`Branch `Permanent
~id:"contract.unspendable_contract" ~id:"contract.unspendable_contract"
~title:"Unspendable contract" ~title:"Unspendable contract"
~description:"An operation tried to spend tokens from an unspendable contract" ~description:"An operation tried to spend tokens from an unspendable contract"
~pp:(fun ppf c -> ~pp:(fun ppf c ->
Format.fprintf ppf "The tokens of contract %s can only be spent by its script" Format.fprintf ppf "The tokens of contract %a can only be spent by its script"
(Contract_repr.to_b58check c)) Contract_repr.pp c)
Data_encoding.(obj1 (req "contract" Contract_repr.encoding)) Data_encoding.(obj1 (req "contract" Contract_repr.encoding))
(function Unspendable_contract c -> Some c | _ -> None) (function Unspendable_contract c -> Some c | _ -> None)
(fun c -> Unspendable_contract c) ; (fun c -> Unspendable_contract c) ;
register_error_kind register_error_kind
`Temporary `Temporary
~id:"contract.balance_too_low" ~id:"contract.balance_too_low"
~title:"Too low balance" ~title:"Balance too low"
~description:"An operation tried to spend more tokens than the contract has" ~description:"An operation tried to spend more tokens than the contract has"
~pp:(fun ppf (c, b, a) -> ~pp:(fun ppf (c, b, a) ->
Format.fprintf ppf "Balance of contract %s too low (%a) to spend %a" Format.fprintf ppf "Balance of contract %a too low (%a) to spend %a"
(Contract_repr.to_b58check c) Tez_repr.pp b Tez_repr.pp a) Contract_repr.pp c Tez_repr.pp b Tez_repr.pp a)
Data_encoding.(obj3 Data_encoding.(obj3
(req "contract" Contract_repr.encoding) (req "contract" Contract_repr.encoding)
(req "balance" Tez_repr.encoding) (req "balance" Tez_repr.encoding)
(req "amount" Tez_repr.encoding)) (req "amount" Tez_repr.encoding))
(function Balance_too_low (c, b, a) -> Some (c, b, a) | _ -> None) (function Balance_too_low (c, b, a) -> Some (c, b, a) | _ -> None)
(fun (c, b, a) -> Balance_too_low (c, b, a)) ; (fun (c, b, a) -> Balance_too_low (c, b, a)) ;
register_error_kind
`Temporary
~id:"contract.cannot_pay_storage_fee"
~title:"Cannot pay storage fee"
~description:"The storage fee is higher than the contract balance"
~pp:(fun ppf (c, b, a) ->
Format.fprintf ppf "Balance of contract %a too low (%a) to pay storage fee %a"
Contract_repr.pp c Tez_repr.pp b Tez_repr.pp a)
Data_encoding.(obj3
(req "contract" Contract_repr.encoding)
(req "balance" Tez_repr.encoding)
(req "storage_fee" Tez_repr.encoding))
(function Cannot_pay_storage_fee (c, b, a) -> Some (c, b, a) | _ -> None)
(fun (c, b, a) -> Cannot_pay_storage_fee (c, b, a)) ;
register_error_kind register_error_kind
`Temporary `Temporary
~id:"contract.counter_in_the_future" ~id:"contract.counter_in_the_future"
@ -73,8 +80,8 @@ let () =
~description:"An operation assumed a contract counter in the future" ~description:"An operation assumed a contract counter in the future"
~pp:(fun ppf (contract, exp, found) -> ~pp:(fun ppf (contract, exp, found) ->
Format.fprintf ppf Format.fprintf ppf
"Counter %ld not yet reached for contract %s (expected %ld)" "Counter %ld not yet reached for contract %a (expected %ld)"
found (Contract_repr.to_b58check contract) exp) found Contract_repr.pp contract exp)
Data_encoding. Data_encoding.
(obj3 (obj3
(req "contract" Contract_repr.encoding) (req "contract" Contract_repr.encoding)
@ -89,26 +96,57 @@ let () =
~description:"An operation assumed a contract counter in the past" ~description:"An operation assumed a contract counter in the past"
~pp:(fun ppf (contract, exp, found) -> ~pp:(fun ppf (contract, exp, found) ->
Format.fprintf ppf Format.fprintf ppf
"Counter %ld already used for contract %s (expected %ld)" "Counter %ld already used for contract %a (expected %ld)"
found (Contract_repr.to_b58check contract) exp) found Contract_repr.pp contract exp)
Data_encoding. Data_encoding.
(obj3 (obj3
(req "contract" Contract_repr.encoding) (req "contract" Contract_repr.encoding)
(req "expected" int32) (req "expected" int32)
(req "found" int32)) (req "found" int32))
(function Counter_in_the_past (c, x, y) -> Some (c, x, y) | _ -> None) (function Counter_in_the_past (c, x, y) -> Some (c, x, y) | _ -> None)
(fun (c, x, y) -> Counter_in_the_past (c, x, y)) (fun (c, x, y) -> Counter_in_the_past (c, x, y)) ;
register_error_kind
`Temporary
~id:"contract.non_existing_contract"
~title:"Non existing contract"
~description:"A non default contract handle is not present in the context \
(either it never was or it has been destroyed)"
~pp:(fun ppf contract ->
Format.fprintf ppf "Contract %a does not exist"
Contract_repr.pp contract)
Data_encoding.(obj1 (req "contract" Contract_repr.encoding))
(function Non_existing_contract c -> Some c | _ -> None)
(fun c -> Non_existing_contract c) ;
register_error_kind
`Permanent
~id:"contract.undelagatable_contract"
~title:"Non delegatable contract"
~description:"Tried to delegate a default contract \
or a non delegatable originated contract"
~pp:(fun ppf contract ->
Format.fprintf ppf "Contract %a is not delegatable"
Contract_repr.pp contract)
Data_encoding.(obj1 (req "contract" Contract_repr.encoding))
(function Non_existing_contract c -> Some c | _ -> None)
(fun c -> Non_existing_contract c) ;
register_error_kind
`Permanent
~id:"contract.failure"
~title:"Contract storage failure"
~description:"Unexpected contract storage error"
~pp:(fun ppf s -> Format.fprintf ppf "Contract_storage.Failure %S" s)
Data_encoding.(obj1 (req "message" string))
(function Failure s -> Some s | _ -> None)
(fun s -> Failure s)
let failwith msg = fail (Failure msg) let failwith msg = fail (Failure msg)
let create_base c contract ~balance ~manager ~delegate ~script ~spendable ~delegatable = let create_base c contract ~balance ~manager ~delegate ?script ~spendable ~delegatable =
(match Contract_repr.is_default contract with (match Contract_repr.is_default contract with
| None -> return 0l | None -> return 0l
| Some _ -> Storage.Contract.Global_counter.get c) >>=? fun counter -> | Some _ -> Storage.Contract.Global_counter.get c) >>=? fun counter ->
Storage.Contract.Balance.init c contract balance >>=? fun c -> Storage.Contract.Balance.init c contract balance >>=? fun c ->
Storage.Contract.Manager.init c contract manager >>=? fun c -> Storage.Contract.Manager.init c contract manager >>=? fun c ->
(* TODO, to answer:
If the contract is not delegatable, can it be created with a delegate ? *)
begin begin
match delegate with match delegate with
| None -> return c | None -> return c
@ -120,26 +158,28 @@ let create_base c contract ~balance ~manager ~delegate ~script ~spendable ~deleg
Storage.Contract.Assets.init c contract Asset_repr.Map.empty >>=? fun c -> Storage.Contract.Assets.init c contract Asset_repr.Map.empty >>=? fun c ->
Storage.Contract.Counter.init c contract counter >>=? fun c -> Storage.Contract.Counter.init c contract counter >>=? fun c ->
(match script with (match script with
| Script_repr.Script { code ; storage } -> | Some ({ Script_repr.code ; storage }, (code_fees, storage_fees)) ->
Storage.Contract.Code.init c contract code >>=? fun c -> Storage.Contract.Code.init c contract code >>=? fun c ->
Storage.Contract.Storage.init c contract storage Storage.Contract.Storage.init c contract storage >>=? fun c ->
| No_script -> Storage.Contract.Code_fees.init c contract code_fees >>=? fun c ->
Storage.Contract.Storage_fees.init c contract storage_fees
| None ->
return c) >>=? fun c -> return c) >>=? fun c ->
Roll_storage.Contract.init c contract >>=? fun c -> Roll_storage.Contract.init c contract >>=? fun c ->
Roll_storage.Contract.add_amount c contract balance >>=? fun c -> Roll_storage.Contract.add_amount c contract balance >>=? fun c ->
Storage.Contract.Set.add c contract >>=? fun c -> Storage.Contract.Set.add c contract >>=? fun c ->
Lwt.return (Ok (c, contract)) Lwt.return (Ok (c, contract))
let create c nonce ~balance ~manager ~delegate ~script ~spendable ~delegatable = let create c nonce ~balance ~manager ~delegate ?script ~spendable ~delegatable =
let contract = Contract_repr.originated_contract nonce in let contract = Contract_repr.originated_contract nonce in
create_base c contract ~balance ~manager ~delegate ~script ~spendable ~delegatable >>=? fun (ctxt, contract) -> create_base c contract ~balance ~manager ~delegate ?script ~spendable ~delegatable >>=? fun (ctxt, contract) ->
return (ctxt, contract, Contract_repr.incr_origination_nonce nonce) return (ctxt, contract, Contract_repr.incr_origination_nonce nonce)
let create_default c manager ~balance = let create_default c manager ~balance =
let contract = Contract_repr.default_contract manager in create_base c (Contract_repr.default_contract manager)
create_base c contract ~manager ~delegate:(Some manager) ~balance ~manager ~delegate:(Some manager)
~spendable:true ~delegatable:false ~script:Script_repr.No_script ?script:None
~balance ~spendable:true ~delegatable:false
let delete c contract = let delete c contract =
Storage.Contract.Balance.get c contract >>=? fun balance -> Storage.Contract.Balance.get c contract >>=? fun balance ->
@ -153,6 +193,8 @@ let delete c contract =
Storage.Contract.Counter.delete c contract >>=? fun c -> Storage.Contract.Counter.delete c contract >>=? fun c ->
Storage.Contract.Code.remove c contract >>= fun c -> Storage.Contract.Code.remove c contract >>= fun c ->
Storage.Contract.Storage.remove c contract >>= fun c -> Storage.Contract.Storage.remove c contract >>= fun c ->
Storage.Contract.Code_fees.remove c contract >>= fun c ->
Storage.Contract.Storage_fees.remove c contract >>= fun c ->
Storage.Contract.Set.del c contract Storage.Contract.Set.del c contract
let exists c contract = let exists c contract =
@ -163,6 +205,11 @@ let exists c contract =
| None -> return false | None -> return false
| Some _ -> return true | Some _ -> return true
let must_exist c contract =
exists c contract >>=? function
| true -> return ()
| false -> fail (Non_existing_contract contract)
let list c = let list c =
Storage.Contract.Set.elements c Storage.Contract.Set.elements c
@ -186,8 +233,8 @@ let get_script c contract =
Storage.Contract.Code.get_option c contract >>=? fun code -> Storage.Contract.Code.get_option c contract >>=? fun code ->
Storage.Contract.Storage.get_option c contract >>=? fun storage -> Storage.Contract.Storage.get_option c contract >>=? fun storage ->
match code, storage with match code, storage with
| None, None -> return Script_repr.No_script | None, None -> return None
| Some code, Some storage -> return (Script_repr.Script { code ; storage }) | Some code, Some storage -> return (Some { Script_repr.code ; storage })
| None, Some _ | Some _, None -> failwith "get_script" | None, Some _ | Some _, None -> failwith "get_script"
let get_counter c contract = let get_counter c contract =
@ -210,11 +257,6 @@ let get_manager c contract =
let get_delegate_opt = Roll_storage.get_contract_delegate let get_delegate_opt = Roll_storage.get_contract_delegate
let get_delegate c contract =
get_delegate_opt c contract >>=? function
| None -> fail No_delegate
| Some delegate -> return delegate
let get_balance c contract = let get_balance c contract =
Storage.Contract.Balance.get_option c contract >>=? function Storage.Contract.Balance.get_option c contract >>=? function
| None -> begin | None -> begin
@ -255,7 +297,7 @@ let set_delegate c contract delegate =
(* A contract delegate can be set only if the contract is delegatable *) (* A contract delegate can be set only if the contract is delegatable *)
Storage.Contract.Delegatable.get c contract >>=? fun delegatable -> Storage.Contract.Delegatable.get c contract >>=? fun delegatable ->
if not delegatable if not delegatable
then fail Undelagatable_contract then fail (Undelagatable_contract contract)
else else
match delegate with match delegate with
| None -> | None ->
@ -264,61 +306,56 @@ let set_delegate c contract delegate =
| Some delegate -> | Some delegate ->
Storage.Contract.Delegate.init_set c contract delegate Storage.Contract.Delegate.init_set c contract delegate
let script_storage_fee script = let contract_fee c contract =
match script with Storage.Contract.Code_fees.get_option c contract >>=? fun code_fees ->
| Script_repr.No_script -> return Constants_repr.minimal_contract_balance Storage.Contract.Storage_fees.get_option c contract >>=? fun storage_fees ->
| Script { code ; storage } -> match code_fees, storage_fees with
let storage_fee = Script_repr.storage_cost storage in | (None, Some _) | (Some _, None) -> failwith "contract_fee"
let code_fee = Script_repr.code_cost code in | None, None ->
Lwt.return Tez_repr.(code_fee +? storage_fee) >>=? fun script_fee -> return Constants_repr.minimal_contract_balance
Lwt.return Tez_repr.(Constants_repr.minimal_contract_balance +? script_fee) | Some code_fees, Some storage_fees ->
Lwt.return Tez_repr.(code_fees +? storage_fees) >>=? fun script_fees ->
Lwt.return Tez_repr.(Constants_repr.minimal_contract_balance +? script_fees)
let update_script_storage c contract storage = let update_script_storage_and_fees c contract storage_fees storage =
let open Script_repr in let open Script_repr in
Storage.Contract.Balance.get_option c contract >>=? function Storage.Contract.Balance.get_option c contract >>=? function
| None -> | None ->
(* The contract was destroyed *) (* The contract was destroyed *)
return c return c
| Some balance -> | Some balance ->
get_script c contract >>=? function Storage.Contract.Storage.get c contract >>=? fun { storage_type } ->
| No_script -> failwith "update_script_storage" Storage.Contract.Storage_fees.set c contract storage_fees >>=? fun c ->
| Script { code ; storage = { storage_type } } -> contract_fee c contract >>=? fun fee ->
script_storage_fee fail_unless Tez_repr.(balance > fee)
(Script_repr.Script { code ; storage = { storage; storage_type }}) >>=? fun fee -> (Cannot_pay_storage_fee (contract, balance, fee)) >>=? fun () ->
fail_unless Tez_repr.(balance > fee) Storage.Contract.Storage.set c contract { storage; storage_type }
(Balance_too_low (contract, balance, fee)) >>=? fun () ->
Storage.Contract.Storage.set c contract { storage; storage_type }
let unconditional_spend c contract amount = let spend_from_script c contract amount =
Storage.Contract.Balance.get c contract >>=? fun balance -> Storage.Contract.Balance.get c contract >>=? fun balance ->
match Tez_repr.(balance - amount) with Lwt.return Tez_repr.(balance -? amount) |>
| None -> trace (Balance_too_low (contract, balance, amount)) >>=? fun new_balance ->
fail (Balance_too_low (contract, balance, amount)) contract_fee c contract >>=? fun fee ->
| Some new_balance -> if Tez_repr.(new_balance > fee) then
get_script c contract >>=? fun script -> Storage.Contract.Balance.set c contract new_balance >>=? fun c ->
script_storage_fee script >>=? fun fee -> Roll_storage.Contract.remove_amount c contract amount
if Tez_repr.(fee <= new_balance) then else
Storage.Contract.Balance.set c contract new_balance >>=? fun c -> (* TODO: do we really want to allow overspending ? *)
Roll_storage.Contract.remove_amount c contract amount delete c contract
else
delete c contract
let credit c contract amount = let credit c contract amount =
Storage.Contract.Balance.get_option c contract >>=? function Storage.Contract.Balance.get_option c contract >>=? function
| None -> begin | None -> begin
(* If the contract does not exists and it is a default contract,
create it *)
match Contract_repr.is_default contract with match Contract_repr.is_default contract with
| None -> fail Non_existing_contract | None -> fail (Non_existing_contract contract)
| Some manager -> | Some manager ->
if Tez_repr.(amount < Constants_repr.minimal_contract_balance) if Tez_repr.(amount < Constants_repr.minimal_contract_balance)
then then
(* If this is not enough to maintain the contract alive, (* Not enough to keep a default contract alive: burn the tokens *)
we just drop the money *)
return c return c
else else
(* Otherwise, create the default contract. *)
create_default c manager ~balance:amount >>=? fun (c, _) -> create_default c manager ~balance:amount >>=? fun (c, _) ->
(* TODO: fail_unless Contract_repr.(contract = new_contract) still needed ?? *)
return c return c
end end
| Some balance -> | Some balance ->
@ -339,16 +376,15 @@ let spend c contract amount =
Storage.Contract.Spendable.get c contract >>=? fun spendable -> Storage.Contract.Spendable.get c contract >>=? fun spendable ->
if not spendable if not spendable
then fail (Unspendable_contract contract) then fail (Unspendable_contract contract)
else unconditional_spend c contract amount else spend_from_script c contract amount
let originate c nonce ~balance ~manager ~script ~delegate ~spendable ~delegatable = let originate c nonce ~balance ~manager ?script ~delegate ~spendable ~delegatable =
script_storage_fee script >>=? fun fee -> create c nonce ~balance ~manager ~delegate ?script ~spendable ~delegatable >>=? fun (c, contract, nonce) ->
(* check contract fee *)
contract_fee c contract >>=? fun fee ->
fail_unless Tez_repr.(balance > fee) fail_unless Tez_repr.(balance > fee)
(Initial_amount_too_low (fee, balance)) >>=? fun () -> (Initial_amount_too_low (contract, balance, fee)) >>=? fun () ->
create c nonce ~balance ~manager ~delegate ~script ~spendable ~delegatable return (c, contract, nonce)
let init c = let init c =
Storage.Contract.Global_counter.init c 0l Storage.Contract.Global_counter.init c 0l
let pp fmt c =
Format.pp_print_string fmt (Contract_repr.to_b58check c)

View File

@ -8,19 +8,20 @@
(**************************************************************************) (**************************************************************************)
type error += type error +=
| Balance_too_low of Contract_repr.contract * Tez_repr.t * Tez_repr.t | Initial_amount_too_low of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Permanent *)
| Initial_amount_too_low of Tez_repr.t * Tez_repr.t | Balance_too_low of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Temporary *)
| Counter_in_the_past of Contract_repr.contract * int32 * int32 | Cannot_pay_storage_fee of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Temporary *)
| Counter_in_the_future of Contract_repr.contract * int32 * int32 | Counter_in_the_past of Contract_repr.contract * int32 * int32 (* `Branch *)
| Unspendable_contract of Contract_repr.contract | Counter_in_the_future of Contract_repr.contract * int32 * int32 (* `Temporary *)
| Non_existing_contract | Unspendable_contract of Contract_repr.contract (* `Permanent *)
| No_delegate | Non_existing_contract of Contract_repr.contract (* `Temporary *)
| Undelagatable_contract | Undelagatable_contract of Contract_repr.contract (* `Permanent *)
| Failure of string | Failure of string (* `Permanent *)
val delete : Storage.t -> Contract_repr.t -> Storage.t tzresult Lwt.t val delete : Storage.t -> Contract_repr.t -> Storage.t tzresult Lwt.t
val exists: Storage.t -> Contract_repr.t -> bool tzresult Lwt.t val exists: Storage.t -> Contract_repr.t -> bool tzresult Lwt.t
val must_exist: Storage.t -> Contract_repr.t -> unit tzresult Lwt.t
val list: Storage.t -> Contract_repr.t list tzresult Lwt.t val list: Storage.t -> Contract_repr.t list tzresult Lwt.t
@ -31,19 +32,14 @@ val is_delegatable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t
val is_spendable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t val is_spendable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t
val get_manager: Storage.t -> Contract_repr.t -> Ed25519.Public_key_hash.t tzresult Lwt.t val get_manager: Storage.t -> Contract_repr.t -> Ed25519.Public_key_hash.t tzresult Lwt.t
val get_delegate: Storage.t -> Contract_repr.t -> Ed25519.Public_key_hash.t tzresult Lwt.t
val get_delegate_opt: Storage.t -> Contract_repr.t -> Ed25519.Public_key_hash.t option tzresult Lwt.t val get_delegate_opt: Storage.t -> Contract_repr.t -> Ed25519.Public_key_hash.t option tzresult Lwt.t
val get_balance: Storage.t -> Contract_repr.t -> Tez_repr.t tzresult Lwt.t val get_balance: Storage.t -> Contract_repr.t -> Tez_repr.t tzresult Lwt.t
val get_assets: Storage.t -> Contract_repr.t -> Asset_repr.Map.t tzresult Lwt.t val get_assets: Storage.t -> Contract_repr.t -> Asset_repr.Map.t tzresult Lwt.t
val get_counter: Storage.t -> Contract_repr.t -> int32 tzresult Lwt.t val get_counter: Storage.t -> Contract_repr.t -> int32 tzresult Lwt.t
val get_script: Storage.t -> Contract_repr.t -> Script_repr.t tzresult Lwt.t val get_script: Storage.t -> Contract_repr.t -> Script_repr.t option tzresult Lwt.t
(** Update_script_storage fails if the contract has not enouth tez to val update_script_storage_and_fees: Storage.t -> Contract_repr.t -> Tez_repr.t -> Script_repr.expr -> Storage.t tzresult Lwt.t
store the new data.
It does not fail if the contract does not exists *)
val update_script_storage: Storage.t -> Contract_repr.t -> Script_repr.expr ->
Storage.t tzresult Lwt.t
(** fails if the contract is not delegatable *) (** fails if the contract is not delegatable *)
val set_delegate : Storage.t -> Contract_repr.t -> Ed25519.Public_key_hash.t option -> Storage.t tzresult Lwt.t val set_delegate : Storage.t -> Contract_repr.t -> Ed25519.Public_key_hash.t option -> Storage.t tzresult Lwt.t
@ -53,8 +49,8 @@ val credit : Storage.t -> Contract_repr.t -> Tez_repr.t -> Storage.t tzresult Lw
(** checks that the contract is spendable and decrease_balance *) (** checks that the contract is spendable and decrease_balance *)
val spend : Storage.t -> Contract_repr.t -> Tez_repr.t -> Storage.t tzresult Lwt.t val spend : Storage.t -> Contract_repr.t -> Tez_repr.t -> Storage.t tzresult Lwt.t
(* decrease balance uncondionally *) (** decrease_balance even if the contract is not spendable *)
val unconditional_spend : Storage.t -> Contract_repr.t -> Tez_repr.t -> Storage.t tzresult Lwt.t val spend_from_script : Storage.t -> Contract_repr.t -> Tez_repr.t -> Storage.t tzresult Lwt.t
val issue : val issue :
Storage.t -> Contract_repr.t -> Asset_repr.t -> Ed25519.Public_key_hash.t -> Tez_repr.t -> Storage.t tzresult Lwt.t Storage.t -> Contract_repr.t -> Asset_repr.t -> Ed25519.Public_key_hash.t -> Tez_repr.t -> Storage.t tzresult Lwt.t
@ -64,7 +60,7 @@ val originate :
Contract_repr.origination_nonce -> Contract_repr.origination_nonce ->
balance:Tez_repr.t -> balance:Tez_repr.t ->
manager:Ed25519.Public_key_hash.t -> manager:Ed25519.Public_key_hash.t ->
script:Script_repr.t -> ?script:(Script_repr.t * (Tez_repr.t * Tez_repr.t)) ->
delegate:Ed25519.Public_key_hash.t option -> delegate:Ed25519.Public_key_hash.t option ->
spendable:bool -> spendable:bool ->
delegatable:bool -> delegatable:bool ->
@ -72,5 +68,3 @@ val originate :
val init : val init :
Storage.t -> Storage.t tzresult Lwt.t Storage.t -> Storage.t tzresult Lwt.t
val pp: Format.formatter -> Contract_repr.t -> unit

View File

@ -76,13 +76,12 @@ let pay_mining_bond c
if Compare.Int32.(priority >= Constants.first_free_mining_slot c) if Compare.Int32.(priority >= Constants.first_free_mining_slot c)
then return c then return c
else else
Contract.unconditional_spend c Contract.spend c (Contract.default_contract id) Constants.mining_bond_cost
(Contract.default_contract id) Constants.mining_bond_cost
|> trace Cannot_pay_mining_bond |> trace Cannot_pay_mining_bond
let pay_endorsement_bond c id = let pay_endorsement_bond c id =
let bond = Constants.endorsement_bond_cost in let bond = Constants.endorsement_bond_cost in
Contract.unconditional_spend c (Contract.default_contract id) bond Contract.spend c (Contract.default_contract id) bond
|> trace Cannot_pay_endorsement_bond >>=? fun c -> |> trace Cannot_pay_endorsement_bond >>=? fun c ->
return (c, bond) return (c, bond)

View File

@ -53,7 +53,7 @@ and manager_operation =
| Origination of { | Origination of {
manager: Ed25519.Public_key_hash.t ; manager: Ed25519.Public_key_hash.t ;
delegate: Ed25519.Public_key_hash.t option ; delegate: Ed25519.Public_key_hash.t option ;
script: Script_repr.t ; script: Script_repr.t option ;
spendable: bool ; spendable: bool ;
delegatable: bool ; delegatable: bool ;
credit: Tez_repr.tez ; credit: Tez_repr.tez ;
@ -113,7 +113,7 @@ module Encoding = struct
(opt "spendable" bool) (opt "spendable" bool)
(opt "delegatable" bool) (opt "delegatable" bool)
(opt "delegate" Ed25519.Public_key_hash.encoding) (opt "delegate" Ed25519.Public_key_hash.encoding)
(req "script" Script_repr.encoding)) (opt "script" Script_repr.encoding))
let origination_case tag = let origination_case tag =
case ~tag origination_encoding case ~tag origination_encoding

View File

@ -53,7 +53,7 @@ and manager_operation =
| Origination of { | Origination of {
manager: Ed25519.Public_key_hash.t ; manager: Ed25519.Public_key_hash.t ;
delegate: Ed25519.Public_key_hash.t option ; delegate: Ed25519.Public_key_hash.t option ;
script: Script_repr.t ; script: Script_repr.t option ;
spendable: bool ; spendable: bool ;
delegatable: bool ; delegatable: bool ;
credit: Tez_repr.tez ; credit: Tez_repr.tez ;

View File

@ -13,6 +13,9 @@ open Script
open Script_typed_ir open Script_typed_ir
open Script_ir_translator open Script_ir_translator
let dummy_code_fee = Tez.zero
let dummy_storage_fee = Tez.zero
(* ---- Run-time errors -----------------------------------------------------*) (* ---- Run-time errors -----------------------------------------------------*)
type error += Quota_exceeded type error += Quota_exceeded
@ -394,65 +397,64 @@ let rec interp
logged_return (Item (manager, rest), qta - 1, ctxt) logged_return (Item (manager, rest), qta - 1, ctxt)
| Transfer_tokens storage_type, | Transfer_tokens storage_type,
Item (p, Item (amount, Item ((tp, Unit_t, destination), Item (sto, Empty)))) -> begin Item (p, Item (amount, Item ((tp, Unit_t, destination), Item (sto, Empty)))) -> begin
Contract.unconditional_spend ctxt source amount >>=? fun ctxt -> Contract.spend_from_script ctxt source amount >>=? fun ctxt ->
Lwt.return Tez.(amount -? Constants.origination_burn) >>=? fun amount ->
Contract.credit ctxt destination amount >>=? fun ctxt -> Contract.credit ctxt destination amount >>=? fun ctxt ->
Contract.get_script ctxt destination >>=? fun destination_script -> Contract.get_script ctxt destination >>=? fun destination_script ->
let sto = unparse_data storage_type sto in let sto = unparse_data storage_type sto in
Contract.update_script_storage ctxt source sto >>=? fun ctxt -> Contract.update_script_storage_and_fees ctxt source dummy_storage_fee sto >>=? fun ctxt ->
begin match destination_script with begin match destination_script with
| No_script -> | None ->
(* we see non scripted contracts as (unit, unit) contract *) (* we see non scripted contracts as (unit, unit) contract *)
Lwt.return (ty_eq tp Unit_t |> Lwt.return (ty_eq tp Unit_t |>
record_trace (Invalid_contract (loc, destination))) >>=? fun (Eq _) -> record_trace (Invalid_contract (loc, destination))) >>=? fun (Eq _) ->
return (ctxt, qta, origination) return (ctxt, qta, origination)
| Script { code ; storage } -> | Some { code ; storage } ->
let p = unparse_data tp p in let p = unparse_data tp p in
execute origination source destination ctxt storage code amount p qta execute origination source destination ctxt storage code amount p qta
>>=? fun (csto, ret, qta, ctxt, origination) -> >>=? fun (csto, ret, qta, ctxt, origination) ->
Contract.update_script_storage Contract.update_script_storage_and_fees ctxt destination dummy_storage_fee csto >>=? fun ctxt ->
ctxt destination csto >>=? fun ctxt ->
trace trace
(Invalid_contract (loc, destination)) (Invalid_contract (loc, destination))
(parse_data ctxt Unit_t ret) >>=? fun () -> (parse_data ctxt Unit_t ret) >>=? fun () ->
return (ctxt, qta, origination) return (ctxt, qta, origination)
end >>=? fun (ctxt, qta, origination) -> end >>=? fun (ctxt, qta, origination) ->
Contract.get_script ctxt source >>=? (function Contract.get_script ctxt source >>=? (function
| No_script -> assert false | None -> assert false
| Script { storage = { storage } } -> | Some { storage = { storage } } ->
parse_data ctxt storage_type storage >>=? fun sto -> parse_data ctxt storage_type storage >>=? fun sto ->
logged_return ~origination (Item ((), Item (sto, Empty)), qta - 1, ctxt)) logged_return ~origination (Item ((), Item (sto, Empty)), qta - 1, ctxt))
end end
| Transfer_tokens storage_type, | Transfer_tokens storage_type,
Item (p, Item (amount, Item ((tp, tr, destination), Item (sto, Empty)))) -> begin Item (p, Item (amount, Item ((tp, tr, destination), Item (sto, Empty)))) -> begin
Contract.unconditional_spend ctxt source amount >>=? fun ctxt -> Contract.spend_from_script ctxt source amount >>=? fun ctxt ->
Contract.credit ctxt destination amount >>=? fun ctxt -> Contract.credit ctxt destination amount >>=? fun ctxt ->
Contract.get_script ctxt destination >>=? function Contract.get_script ctxt destination >>=? function
| No_script -> fail (Invalid_contract (loc, destination)) | None -> fail (Invalid_contract (loc, destination))
| Script { code ; storage } -> | Some { code ; storage } ->
let sto = unparse_data storage_type sto in let sto = unparse_data storage_type sto in
Contract.update_script_storage ctxt source sto >>=? fun ctxt -> Contract.update_script_storage_and_fees ctxt source dummy_storage_fee sto >>=? fun ctxt ->
let p = unparse_data tp p in let p = unparse_data tp p in
execute origination source destination ctxt storage code amount p qta execute origination source destination ctxt storage code amount p qta
>>=? fun (sto, ret, qta, ctxt, origination) -> >>=? fun (sto, ret, qta, ctxt, origination) ->
Contract.update_script_storage Contract.update_script_storage_and_fees ctxt destination dummy_storage_fee sto >>=? fun ctxt ->
ctxt destination sto >>=? fun ctxt ->
trace trace
(Invalid_contract (loc, destination)) (Invalid_contract (loc, destination))
(parse_data ctxt tr ret) >>=? fun v -> (parse_data ctxt tr ret) >>=? fun v ->
Contract.get_script ctxt source >>=? (function Contract.get_script ctxt source >>=? (function
| No_script -> assert false | None -> assert false
| Script { storage = { storage } } -> | Some { storage = { storage } } ->
parse_data ctxt storage_type storage >>=? fun sto -> parse_data ctxt storage_type storage >>=? fun sto ->
logged_return ~origination (Item (v, Item (sto, Empty)), qta - 1, ctxt)) logged_return ~origination (Item (v, Item (sto, Empty)), qta - 1, ctxt))
end end
| Create_account, | Create_account,
Item (manager, Item (delegate, Item (delegatable, Item (credit, rest)))) -> Item (manager, Item (delegate, Item (delegatable, Item (credit, rest)))) ->
Contract.unconditional_spend ctxt source credit >>=? fun ctxt -> Contract.spend_from_script ctxt source credit >>=? fun ctxt ->
Lwt.return Tez.(credit -? Constants.origination_burn) >>=? fun balance -> Lwt.return Tez.(credit -? Constants.origination_burn) >>=? fun balance ->
Contract.originate ctxt Contract.originate ctxt
origination origination
~manager ~delegate ~balance ~manager ~delegate ~balance
~script:No_script ~spendable:true ~delegatable >>=? fun (ctxt, contract, origination) -> ?script:None ~spendable:true ~delegatable >>=? fun (ctxt, contract, origination) ->
logged_return ~origination (Item ((Unit_t, Unit_t, contract), rest), qta - 1, ctxt) logged_return ~origination (Item ((Unit_t, Unit_t, contract), rest), qta - 1, ctxt)
| Create_contract (g, p, r), | Create_contract (g, p, r),
Item (manager, Item (delegate, Item (delegatable, Item (credit, Item (manager, Item (delegate, Item (delegatable, Item (credit,
@ -460,19 +462,13 @@ let rec interp
let code, storage = let code, storage =
{ code; arg_type = unparse_ty p; ret_type = unparse_ty r; storage_type = unparse_ty g }, { code; arg_type = unparse_ty p; ret_type = unparse_ty r; storage_type = unparse_ty g },
{ storage = unparse_data g init; storage_type = unparse_ty g } in { storage = unparse_data g init; storage_type = unparse_ty g } in
let storage_fee = Script.storage_cost storage in Contract.spend_from_script ctxt source credit >>=? fun ctxt ->
let code_fee = Script.code_cost code in
Lwt.return Tez.(code_fee +? storage_fee) >>=? fun script_fee ->
Lwt.return Tez.(script_fee +?
Constants.origination_burn) >>=? fun total_fee ->
fail_unless Tez.(credit > total_fee)
(Contract.Initial_amount_too_low (total_fee, credit)) >>=? fun () ->
Contract.unconditional_spend ctxt source credit >>=? fun ctxt ->
Lwt.return Tez.(credit -? Constants.origination_burn) >>=? fun balance -> Lwt.return Tez.(credit -? Constants.origination_burn) >>=? fun balance ->
Contract.originate ctxt Contract.originate ctxt
origination origination
~manager ~delegate ~balance ~manager ~delegate ~balance
~script:(Script { code ; storage }) ~spendable:true ~delegatable ~script:({ code ; storage }, (dummy_code_fee, dummy_storage_fee))
~spendable:true ~delegatable
>>=? fun (ctxt, contract, origination) -> >>=? fun (ctxt, contract, origination) ->
logged_return ~origination (Item ((p, r, contract), rest), qta - 1, ctxt) logged_return ~origination (Item ((p, r, contract), rest), qta - 1, ctxt)
| Balance, rest -> | Balance, rest ->

View File

@ -14,6 +14,9 @@ type error += Overflow of Script.location
type error += Reject of Script.location type error += Reject of Script.location
type error += Division_by_zero of Script.location type error += Division_by_zero of Script.location
val dummy_code_fee : Tez.t
val dummy_storage_fee : Tez.t
(* calling convention : (* calling convention :
((amount, arg), globals)) -> (ret, globals) *) ((amount, arg), globals)) -> (ret, globals) *)

View File

@ -1388,14 +1388,14 @@ and parse_contract
trace trace
(Invalid_contract (loc, contract)) @@ (Invalid_contract (loc, contract)) @@
Contract.get_script ctxt contract >>=? function Contract.get_script ctxt contract >>=? function
| No_script -> | None ->
Lwt.return Lwt.return
(ty_eq arg Unit_t >>? fun (Eq _) -> (ty_eq arg Unit_t >>? fun (Eq _) ->
ty_eq ret Unit_t >>? fun (Eq _) -> ty_eq ret Unit_t >>? fun (Eq _) ->
let contract : (arg, ret) typed_contract = let contract : (arg, ret) typed_contract =
(arg, ret, contract) in (arg, ret, contract) in
ok contract) ok contract)
| Script { code = { arg_type; ret_type} } -> | Some { code = { arg_type; ret_type} } ->
Lwt.return Lwt.return
(parse_ty arg_type >>? fun (Ex_ty targ) -> (parse_ty arg_type >>? fun (Ex_ty targ) ->
parse_ty ret_type >>? fun (Ex_ty tret) -> parse_ty ret_type >>? fun (Ex_ty tret) ->

View File

@ -110,9 +110,6 @@ type storage =
{ storage : expr ; { storage : expr ;
storage_type : expr } storage_type : expr }
let storage_cost _ = Tez_repr.of_cents_exn 50L (* FIXME *)
let code_cost _ = Tez_repr.of_cents_exn 50L (* FIXME *)
open Data_encoding open Data_encoding
let storage_encoding = let storage_encoding =
@ -140,22 +137,14 @@ let hash_expr data =
Script_expr_hash.(hash_bytes [ bytes ] |> to_b58check) Script_expr_hash.(hash_bytes [ bytes ] |> to_b58check)
type t = type t =
| No_script { code : code ;
| Script of { storage : storage }
code: code ;
storage: storage ;
}
let encoding = let encoding =
let open Data_encoding in let open Data_encoding in
union ~tag_size:`Uint8 [ conv
case ~tag:0 empty (function { code ; storage } -> (code, storage))
(function No_script -> Some () | _ -> None) (fun (code, storage) -> { code ; storage })
(fun () -> No_script) ; (obj2
case ~tag:1 (req "code" code_encoding)
(obj2 (req "storage" storage_encoding))
(req "code" code_encoding)
(req "storage" storage_encoding))
(function Script { code ; storage } -> Some (code, storage) | _ -> None)
(fun (code, storage) -> Script { code ; storage })
]

View File

@ -7,8 +7,6 @@
(* *) (* *)
(**************************************************************************) (**************************************************************************)
(** Tezos protocol 1234abc1212 - untyped script representation *)
type location = type location =
int int
@ -29,11 +27,8 @@ type storage =
storage_type : expr } storage_type : expr }
type t = type t =
| No_script { code : code ;
| Script of { storage : storage }
code: code ;
storage: storage ;
}
val location_encoding : location Data_encoding.t val location_encoding : location Data_encoding.t
val expr_encoding : expr Data_encoding.t val expr_encoding : expr Data_encoding.t
@ -41,7 +36,4 @@ val storage_encoding : storage Data_encoding.t
val code_encoding : code Data_encoding.t val code_encoding : code Data_encoding.t
val encoding : t Data_encoding.t val encoding : t Data_encoding.t
val storage_cost : storage -> Tez_repr.tez
val code_cost : code -> Tez_repr.tez
val hash_expr : expr -> string val hash_expr : expr -> string

View File

@ -253,7 +253,7 @@ module Context = struct
RPC.service RPC.service
~description: "Access the code and data of the contract." ~description: "Access the code and data of the contract."
~input: empty ~input: empty
~output: (wrap_tzerror Script.encoding) ~output: (wrap_tzerror (option Script.encoding))
RPC.Path.(custom_root / "context" / "contracts" /: Contract.arg / "script") RPC.Path.(custom_root / "context" / "contracts" /: Contract.arg / "script")
let assets custom_root = let assets custom_root =
@ -268,7 +268,7 @@ module Context = struct
balance: Tez.t ; balance: Tez.t ;
spendable: bool ; spendable: bool ;
delegate: bool * public_key_hash option ; delegate: bool * public_key_hash option ;
script: Script.t ; script: Script.t option ;
assets: Asset.Map.t ; assets: Asset.Map.t ;
counter: int32 ; counter: int32 ;
} }
@ -291,7 +291,7 @@ module Context = struct
(req "delegate" @@ obj2 (req "delegate" @@ obj2
(req "setable" bool) (req "setable" bool)
(opt "value" Ed25519.Public_key_hash.encoding)) (opt "value" Ed25519.Public_key_hash.encoding))
(dft "script" Script.encoding No_script) (opt "script" Script.encoding)
(req "assets" Asset.Map.encoding) (req "assets" Asset.Map.encoding)
(req "counter" int32)) (req "counter" int32))
RPC.Path.(custom_root / "context" / "contracts" /: Contract.arg) RPC.Path.(custom_root / "context" / "contracts" /: Contract.arg)

View File

@ -117,6 +117,8 @@ module Key = struct
let counter c = contract_store c ["counter"] let counter c = contract_store c ["counter"]
let code c = contract_store c ["code"] let code c = contract_store c ["code"]
let storage c = contract_store c ["storage"] let storage c = contract_store c ["storage"]
let code_fees c = contract_store c ["code_fees"]
let storage_fees c = contract_store c ["storage_fees"]
end end
module Vote = struct module Vote = struct
@ -330,6 +332,24 @@ module Contract = struct
let encoding = Script_repr.storage_encoding let encoding = Script_repr.storage_encoding
end) end)
module Code_fees =
Make_indexed_data_storage(struct
type key = Contract_repr.t
type value = Tez_repr.t
let name = "contract code fees"
let key = Key.Contract.code_fees
let encoding = Tez_repr.encoding
end)
module Storage_fees =
Make_indexed_data_storage(struct
type key = Contract_repr.t
type value = Tez_repr.t
let name = "contract storage fees"
let key = Key.Contract.storage_fees
let encoding = Tez_repr.encoding
end)
end end
(** Votes **) (** Votes **)

View File

@ -174,6 +174,16 @@ module Contract : sig
and type value = Script_repr.storage and type value = Script_repr.storage
and type context := t and type context := t
module Code_fees : Indexed_data_storage
with type key = Contract_repr.t
and type value = Tez_repr.t
and type context := t
module Storage_fees : Indexed_data_storage
with type key = Contract_repr.t
and type value = Tez_repr.t
and type context := t
end end
(** Votes *) (** Votes *)

View File

@ -126,11 +126,8 @@ module Script : sig
} }
type t = type t =
| No_script { code : code ;
| Script of { storage : storage }
code: code ;
storage: storage ;
}
val location_encoding: location Data_encoding.t val location_encoding: location Data_encoding.t
val expr_encoding: expr Data_encoding.t val expr_encoding: expr Data_encoding.t
@ -138,10 +135,7 @@ module Script : sig
val code_encoding: code Data_encoding.t val code_encoding: code Data_encoding.t
val encoding: t Data_encoding.t val encoding: t Data_encoding.t
val storage_cost: storage -> Tez.t val hash_expr : expr -> string
val code_cost: code -> Tez.t
val hash_expr: expr -> string
end end
@ -316,6 +310,8 @@ module Contract : sig
val is_default: contract -> public_key_hash option val is_default: contract -> public_key_hash option
val exists: context -> contract -> bool tzresult Lwt.t val exists: context -> contract -> bool tzresult Lwt.t
val must_exist: context -> contract -> unit tzresult Lwt.t
val list: context -> contract list tzresult Lwt.t val list: context -> contract list tzresult Lwt.t
type origination_nonce type origination_nonce
@ -328,8 +324,6 @@ module Contract : sig
val get_manager: val get_manager:
context -> contract -> public_key_hash tzresult Lwt.t context -> contract -> public_key_hash tzresult Lwt.t
val get_delegate:
context -> contract -> public_key_hash tzresult Lwt.t
val get_delegate_opt: val get_delegate_opt:
context -> contract -> public_key_hash option tzresult Lwt.t context -> contract -> public_key_hash option tzresult Lwt.t
val is_delegatable: val is_delegatable:
@ -337,7 +331,7 @@ module Contract : sig
val is_spendable: val is_spendable:
context -> contract -> bool tzresult Lwt.t context -> contract -> bool tzresult Lwt.t
val get_script: val get_script:
context -> contract -> Script.t tzresult Lwt.t context -> contract -> (Script.t option) tzresult Lwt.t
val get_counter: context -> contract -> int32 tzresult Lwt.t val get_counter: context -> contract -> int32 tzresult Lwt.t
val get_balance: val get_balance:
@ -348,14 +342,14 @@ module Contract : sig
val set_delegate: val set_delegate:
context -> contract -> public_key_hash option -> context tzresult Lwt.t context -> contract -> public_key_hash option -> context tzresult Lwt.t
type error += Initial_amount_too_low of Tez.t * Tez.t type error += Initial_amount_too_low of contract * Tez.t * Tez.t
val originate: val originate:
context -> context ->
origination_nonce -> origination_nonce ->
balance: Tez.t -> balance: Tez.t ->
manager: public_key_hash -> manager: public_key_hash ->
script: Script.t -> ?script: (Script.t * (Tez.t * Tez.t)) ->
delegate: public_key_hash option -> delegate: public_key_hash option ->
spendable: bool -> spendable: bool ->
delegatable: bool -> (context * contract * origination_nonce) tzresult Lwt.t delegatable: bool -> (context * contract * origination_nonce) tzresult Lwt.t
@ -364,7 +358,7 @@ module Contract : sig
val spend: val spend:
context -> contract -> Tez.t -> context tzresult Lwt.t context -> contract -> Tez.t -> context tzresult Lwt.t
val unconditional_spend: val spend_from_script:
context -> contract -> Tez.t -> context tzresult Lwt.t context -> contract -> Tez.t -> context tzresult Lwt.t
val credit: val credit:
@ -373,8 +367,8 @@ module Contract : sig
context -> contract -> context -> contract ->
Asset.t -> public_key_hash -> Tez.t -> context tzresult Lwt.t Asset.t -> public_key_hash -> Tez.t -> context tzresult Lwt.t
val update_script_storage: val update_script_storage_and_fees:
context -> contract -> Script.expr -> context tzresult Lwt.t context -> contract -> Tez.t -> Script.expr -> context tzresult Lwt.t
val increment_counter: val increment_counter:
context -> contract -> context tzresult Lwt.t context -> contract -> context tzresult Lwt.t
@ -474,7 +468,7 @@ and manager_operation =
| Origination of { | Origination of {
manager: public_key_hash ; manager: public_key_hash ;
delegate: public_key_hash option ; delegate: public_key_hash option ;
script: Script.t ; script: Script.t option ;
spendable: bool ; spendable: bool ;
delegatable: bool ; delegatable: bool ;
credit: Tez.t ; credit: Tez.t ;