Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
473 changes: 473 additions & 0 deletions gitlab-pages/docs/advanced/security.md

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions gitlab-pages/docs/advanced/src/security/bank.jsligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Bank {

type storage = big_map<address, tez>;
type return_type = [list<operation>, storage];

// Return a withdrawal
// @entry
const withdraw = (tx_amount: tez, storage: storage): return_type => {
// Verify that the caller has enough balance for the withdrawal
const old_balance = Big_map.find(Tezos.get_sender(), storage);
if (tx_amount > old_balance) {
failwith("Insufficient balance");
}
// Create transaction
const receiver_account = $match((Tezos.get_contract_opt(Tezos.get_sender())), {
"Some": account => account,
"None": () => failwith("Couldn't find account"),
});
const operation = Tezos.Operation.transaction(unit, tx_amount, receiver_account);
// Update balance
const new_balance: tez = Option.value_with_error("Unreachable error; we already compared balance to amount", old_balance - tx_amount);
const new_storage = Big_map.update(Tezos.get_sender(), ["Some" as "Some", new_balance], storage);
return [[operation], new_storage];
}

}
17 changes: 17 additions & 0 deletions gitlab-pages/docs/advanced/src/security/bank.mligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
type storage = (address, tez) big_map
type return_type = operation list * storage

[@entry]
let withdraw (tx_amount : tez) (storage : storage) : return_type =
(* Verify that the caller has enough balance for the withdrawal *)
let old_balance = Big_map.find (Tezos.get_sender ()) storage in
let _ = if tx_amount > old_balance then failwith "Insufficient balance" in
(* Create transaction *)
let receiver_account = match Tezos.get_contract_opt (Tezos.get_sender ()) with
Some account -> account
| None -> failwith "Couldn't find account" in
let operation = Tezos.Operation.transaction unit tx_amount receiver_account in
(* Update balance *)
let new_balance : tez = Option.value_with_error "Unreachable error; we already compared balance to amount" (old_balance - tx_amount) in
let new_storage = Big_map.update (Tezos.get_sender ()) (Some new_balance) storage in
[operation], new_storage
41 changes: 41 additions & 0 deletions gitlab-pages/docs/advanced/src/security/rewardswithflaw.jsligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace RewardsWithFlaw {

export type storage = {
owner: address,
beneficiaries: list<address>,
};

// Send rewards to one address
const send_one_reward = (beneficiary_addr: address): operation => {
const contract_opt =
Tezos.get_contract_opt( beneficiary_addr);
const beneficiary = $match(contract_opt, {
"Some": contract => contract,
"None": () => failwith("CONTRACT_NOT_FOUND"),
});
return Tezos.Operation.transaction(unit, 5 as tez, beneficiary);
}

// Send rewards to all beneficiaries
// @entry
const send_rewards = (_: unit, storage: storage): [list<operation>, storage] => {
if (Tezos.get_sender() != storage.owner) {
failwith("Not the owner");
}
const operations = List.map(send_one_reward, storage.beneficiaries);
return [operations, storage];
}

// @entry
const change_owner = (new_owner: address, storage: storage): [list<operation>, storage] => {
// Verify that the sender is the admin
if (Tezos.get_sender() != storage.owner) {
failwith("Not the owner");
}
return [[], {
owner: new_owner,
beneficiaries: storage.beneficiaries,
}];
}

}
32 changes: 32 additions & 0 deletions gitlab-pages/docs/advanced/src/security/rewardswithflaw.mligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module RewardsWithFlaw = struct

type storage = {
owner : address;
beneficiaries : address list
}

(* Send rewards to one address *)
let send_one_reward (beneficiary_addr : address) : operation =
let contract_opt =
Tezos.get_contract_opt beneficiary_addr in
let beneficiary =
match contract_opt with
Some contract -> contract
| None -> (failwith "CONTRACT_NOT_FOUND" : unit contract) in
Tezos.Operation.transaction () 5tez beneficiary

(* Send rewards to all beneficiaries *)
[@entry]
let send_rewards (_ : unit) (storage : storage) : operation list * storage =
if Tezos.get_sender () <> storage.owner
then failwith "Not the owner"
else let operations = List.map send_one_reward storage.beneficiaries in
operations, storage

[@entry]
let change_owner (new_owner : address) (storage : storage) : operation list * storage =
(* Verify that the sender is the admin *)
let _ = if Tezos.get_sender () <> storage.owner then failwith "Not the owner" in
[], { storage with owner = new_owner }

end
55 changes: 55 additions & 0 deletions gitlab-pages/docs/advanced/src/security/walletwithflaw.jsligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
namespace WalletWithFlaw {

// Variant for two types of transactions
type transaction =
["Deposit", [address, tez]]
| ["Withdrawal", [address, tez]];

type storage = {
owner: address,
transactionLog: list<transaction>,
};

type return_type = [list<operation>, storage];

// Receive a deposit
// @entry
const deposit = (_: unit, storage: storage): return_type => {
// Verify that tez was sent
if (Tezos.get_amount() == (0 as tez)) {
failwith("Send tez to deposit");
}
// Add log entry
const newLogEntry: transaction = ["Deposit" as "Deposit", [Tezos.get_sender(), Tezos.get_amount()]];
return [[], {
owner: storage.owner,
transactionLog: [newLogEntry, ...storage.transactionLog],
}];
}

// Return a withdrawal
// @entry
const withdraw = (param : [address, tez], storage : storage): return_type => {
const [tx_destination, tx_amount] = param;
// Verify that the sender is the admin
if (Tezos.get_sender() != storage.owner) {
failwith("Not the owner");
}
// Verify that no tez was sent
if (Tezos.get_amount() != (0 as tez)) {
failwith("Don't send tez to this entrypoint");
}
// Create transaction
const callee = Tezos.get_contract_opt(tx_destination);
const operation = $match(callee, {
"Some": contract => (() => Tezos.Operation.transaction(unit, tx_amount, contract))(),
"None": () => failwith("Couldn't send withdrawal to that address"),
});
// Add log entry and return operation and new log
const newLogEntry: transaction = ["Withdrawal" as "Withdrawal", [tx_destination, tx_amount]];
return [[operation], {
owner: storage.owner,
transactionLog: [newLogEntry, ...storage.transactionLog],
}];
}
}
42 changes: 42 additions & 0 deletions gitlab-pages/docs/advanced/src/security/walletwithflaw.mligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module WalletWithFlaw = struct

(* Variant for two types of transactions *)
type transaction =
Deposit of address * tez
| Withdrawal of address * tez

type storage = {
owner : address;
transactionLog : transaction list
}

type return_type = operation list * storage

(* Receive a deposit *)
[@entry]
let deposit (_ : unit) (storage : storage) : return_type =
(* Verify that tez was sent *)
let _ = if Tezos.get_amount () = 0tez then failwith "Send tez to deposit" in
(* Add log entry *)
let newLogEntry : transaction = Deposit (Tezos.get_sender (), Tezos.get_amount ()) in
[], { storage with transactionLog = newLogEntry :: storage.transactionLog }

(* Return a withdrawal *)
[@entry]
let withdraw (tx_destination, tx_amount : address * tez) (storage : storage) : return_type =
(* Verify that the sender is the admin *)
let _ = if Tezos.get_sender () <> storage.owner then failwith "Not the owner" in
(* Verify that no tez was sent *)
let _ = if Tezos.get_amount () <> 0tez then failwith "Don't send tez to this entrypoint" in
(* Create transaction *)
let callee = Tezos.get_contract_opt tx_destination in
let operation = match callee with
Some contract ->
Tezos.Operation.transaction () tx_amount contract
| None -> failwith "Couldn't send withdrawal to that address"
in
(* Add log entry and return operation and new log *)
let newLogEntry : transaction = Withdrawal (tx_destination, tx_amount) in
[operation], { storage with transactionLog = newLogEntry :: storage.transactionLog }

end
2 changes: 1 addition & 1 deletion gitlab-pages/docs/intro/ligo-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Your choice to learn LIGO is already available:
You will need a deeper comprehension:
- Teach yourself how to structure your code with [Combining code](../syntax/modules) section
- Learn how to [write tests](../testing) we strongly encourage to use [breathalyzer library from the LIGO registry.](https://packages.ligolang.org/package/ligo-breathalyzer)
- Understand how to [secure a contract](../tutorials/security)
- Understand how to [secure a contract](../advanced/security)

### Dig deeper

Expand Down
2 changes: 1 addition & 1 deletion gitlab-pages/docs/syntax/contracts/entrypoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ const owner_only = (action: parameter, storage: storage): result => {
:::note

The entrypoint in the previous example uses `Tezos.get_sender` instead of `Tezos.get_source` to prevent a security flaw.
For more information, see the [Security tutorial](../../tutorials/security/security.md#incorrect-authorisation-checks).
For more information, see [Security](../../advanced/security).

:::

Expand Down
Loading