Categories
C and Assembler

Variadic Templates in C++

C++ has always been a language revered for its flexibility, depth, and low-level power. It’s a language that enables a variety of programming styles and paradigms, from object-oriented to template meta-programming. One feature of C++ that perfectly encapsulates its versatile nature is variadic templates. Introduced in C++11, variadic templates extend the power of template programming to allow functions, classes, and structures to accept an arbitrary number of arguments in a type-safe way.

What are Variadic Templates?

Variadic templates in C++ are templates that can take an arbitrary number of arguments. They’re named “variadic” because the number of arguments can vary per invocation.

template<typename... Args>
void func(Args... args) {
    // Implementation...
}

In the code above, Args is a template parameter pack, and args is a function parameter pack. The … is the pack expansion operator.

Using Variadic Templates

Let’s dive into some practical examples to get a better sense of how these templates work. Imagine we want to create a function print_all to print any number of arguments of any types. We could use variadic templates for this:

#include <iostream>

template<typename... Args>
void print_all(Args... args) {
    (std::cout << ... << args) << '\n';
}

Now we can use print_all to print any number of arguments of any types:

print_all("Hello, ", "world!", " ", 1, 2, 3);  // Prints: Hello, world! 123

In the function print_all, the expression (std::cout << … << args) is a fold expression, a feature added in C++17. It’s equivalent to std::cout << arg1 << arg2 << arg3 << ….

Advanced Use Case: Compile-time Summation

Variadic templates shine in compile-time computations. Let’s create a function that calculates the sum of all arguments at compile time:

template<typename... Args>
constexpr auto sum(Args... args) {
    return (... + args);
}

This function uses a fold expression to add all arguments together. Here’s how we could use it:

constexpr auto result = sum(1, 2, 3, 4, 5);
std::cout << result << '\n';  // Prints: 15

The sum function accepts any number of arguments and sums them up. Note the use of constexpr which makes the function usable in compile-time contexts.

Conclusion

Variadic templates, just like many other C++ features, are a powerful tool that gives programmers a lot of flexibility. They open up a world of possibilities for writing generic, reusable, and type-safe code. With the power of variadic templates, you can create functions and classes that can accept any number of arguments of any types.

As with any powerful tool, however, variadic templates should be used judiciously. They make code more complex and harder to understand, and they should only be used when necessary. When used correctly, they can greatly improve the expressiveness and reusability of your C++ code.

The journey to mastering variadic templates in C++ is as fascinating as it is rewarding, offering a glimpse into the depth and versatility of the language. Happy coding!

Categories
Kafka

Kafka: Replication Lag

Monitoring replication lag in a Kafka cluster is crucial in order to ensure data availability and durability. Replication lag occurs when the replicas of a partition are not in sync with the leader, which can occur due to network delays or a slow follower.

When replication lag happens, it means that the follower replicas are not able to keep up with the writes being made to the leader. This can cause the replicas to fall behind the leader, which can result in data loss if the leader broker fails before the replicas have caught up. Furthermore, it can also cause inconsistencies in the data stored on the replicas, which can lead to issues when the replicas are promoted to the leader.

There are several causes of replication lag:

  • Network Latency: If the network between the leader and the replicas is slow or unreliable, it can cause replication lag. This can happen if there are high levels of network congestion or if the network is not configured correctly.
  • Disk I/O: If the replica’s disk I/O is slow or saturated, it can cause replication lag. This can happen if the replica is running on a slow or overloaded disk, or if the replica is running on a machine with limited resources.
  • CPU Utilization: If the replica’s CPU is overutilized, it can cause replication lag. This can happen if the replica is running on a machine with limited resources or if the replica is running other processes that are consuming CPU resources.
  • Memory Pressure: If the replica is running low on memory, it can cause replication lag. This can happen if the replica is running on a machine with limited resources, or if the replica is running other processes that are consuming memory resources.

To monitor replication lag, one can use kafka-consumer-groups command and check the lag for each partition and replica. The lag is the number of messages the replica is behind the leader. Monitoring replication lag regularly is important to detect and resolve issues as soon as they arise.

To mitigate the replication lag, one can:

  • Increase the resources available to the replicas such as CPU, Memory and disk I/O
  • Optimize the network between the leader and replicas
  • Tune the replication factor and consistency guarantees
  • Use auto-failover mechanism which automatically elect a new leader for a partition when the current leader goes down.

In summary, replication lag is an important aspect to monitor in a Kafka cluster. It can impact the availability and durability of the data stored in the system. Monitoring replication lag regularly is important to detect and resolve issues as soon as they arise. And there are several ways to mitigate it.

Auto-failover mechanism

An example of an automatic failover mechanism is the Kafka MirrorMaker tool provided by Apache Kafka. This tool is designed to create a replica of a source Kafka cluster on a target Kafka cluster. In the event of a broker failure on the source cluster, MirrorMaker can automatically promote one of the replicas on the target cluster to be the leader for the affected partitions.

This allows the target cluster to continue servicing read and write requests, even if the leader on the source cluster goes down. This not only provides high availability but also minimizes data loss, as clients don’t need to be aware of the leader change.

Here is sample configuration example for MirrorMaker

# list of brokers in the source kafka cluster
source.brokers=192.168.1.100:9092,192.168.1.101:9092,192.168.1.102:9092

# list of brokers in the target kafka cluster
target.brokers=192.168.2.100:9092,192.168.2.101:9092,192.168.2.102:9092

# list of topics to be mirrored
topics=topic1,topic2,topic3

# number of threads to use for mirroring
num.streams=3

# replication factor for mirrored topics on the target cluster
target.replication.factor=3

# enable automatic failover
auto.failover.enabled=true

# time interval to check for broker failures
auto.failover.interval.ms=10000

Confluent’s Kafka-clients library

Another example of automatic failover is Confluent’s Kafka-clients library. This library provides an automatic failover feature for Kafka consumers. In case a consumer loses its connection to the leader broker, the library will automatically try to reconnect to another broker that has the same partition, allowing the consumer to continue reading data from the partition, even if the leader broker goes down.

  1. First, you would include Confluent’s Kafka-clients library in your project by adding the appropriate dependency to your build file (e.g. pom.xml for Maven, build.gradle for Gradle).
    For example:
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>3.3.2</version>
</dependency>

2. Next, you would create a Kafka consumer object by instantiating the KafkaConsumer class and providing the appropriate configuration, such as the list of brokers to connect to, the group ID, and the list of topics to consume.

3. You would then set the enable.auto.commit property to true and to “enable” the automatic failover feature you should set the property reconnect.backoff.ms to a non-zero value, this will cause the consumer to periodically attempt to reconnect to the cluster and it will automatically failover to a new broker if the current broker goes down:

Properties props = new Properties();
props.put("bootstrap.servers", "192.168.1.100:9092,192.168.1.101:9092,192.168.1.102:9092");
props.put("group.id", "myGroupId");
props.put("enable.auto.commit", "true");
props.put("reconnect.backoff.ms", "10000");
// props.put("auto.commit.interval.ms", "1000");
// props.put("auto.offset.reset", "earliest");

KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

4. You can then subscribe to the topics you want to consume from and poll for new messages using the poll() method.

consumer.subscribe(Arrays.asList("topic1", "topic2", "topic3"));

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        // process the record
    }
}

5. If a broker goes down, the library will automatically try to reconnect to another broker that has the same partition, allowing the consumer to continue reading data from the partition, even if the leader broker goes down.

Additionally, you may want to set some configuration properties like session.timeout.ms, max.poll.interval.ms and heartbeat.interval.ms to ensure that the consumer can detect a broker failure in a timely manner and to prevent it from being kicked out of the group because of being considered as dead.

In summary, automatic failover mechanisms are designed to ensure high availability and minimize data loss by automatically electing a new leader for a partition when the current leader goes down. These mechanisms can be implemented through tools such as Kafka MirrorMaker or libraries such as Confluent’s Kafka-clients.

Categories
C and Assembler

Hello World “OS”

While browsing my old hard disk I found this nice peace of code. This C code can start from the boot sector and print Hello World. No dependency is used, it is using its own printf implementation.

// Copyright (C) 2008 Michal Drozd
// All Rights Reserved

// Very simple "operating system" in C which after booting show "Hello World" on display, with its own printf implementation!
// KEEP IN MIND, you can NOT just compile it in standard way as standard program as there is no OS when this program is started, so "PE header" won't be recognised of course, etc.
// Raw compiled code must be placed to boot sector to run it.

// How to build this:
// nasm -f bin -o boot.bin boot.asm
// gcc -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c hello_world_os.c
// ld -m elf_i386 --oformat binary -Ttext 0x7c00 --o hello_world_os.bin hello_world_os.o
// cat boot.bin c_code.bin > os.img
// qemu-system-i386 os.img

#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>


// This function depends on architecture a lot of course!
void bios_video_print_char(char c) {
	auto *video_memory = (uint8_t *) 0xB8000;
	static uint8_t x = 0;
	static uint8_t y = 0;
	switch (c) {
	case '\n': {
		x = 0;
		y++;
		break;
	}
	default: {
		video_memory[2 * (80 * y + x)] = c;
		video_memory[2 * (80 * y + x) + 1] = 0x07; // white on black text
		x++;
		if (x >= 80) {
			x = 0;
			y++;
		}
		break;
	}
	}
	if (y >= 25) {
		for (y = 0; y < 24; y++) {
			for (x = 0; x < 80; x++) {
				video_memory[2 * (80 * y + x)] = video_memory[2 * ((80 * y + 1) + x)];
				video_memory[2 * ((80 * y + 1) + x)] = 0x07; // white on black text
			}
		}
		for (x = 0; x < 80; x++) {
			video_memory[2 * ((80 * 24) + x)] = ' ';
			video_memory[2 * ((80 * 24) + x) + 1] = 0x07; // white on black text
		}
		y = 24;
	}
}

void print(const char* str)
{

	while (*str)
	{
		bios_video_print_char(*str);
		str++;
	}
}

void print_hex(uint32_t num)
{
	char hex[] = "0123456789ABCDEF";
	for (int i = 28; i >= 0; i -= 4)
	{
		bios_video_print_char(hex[(num >> i) & 0xF]);
	}
}
void print_dec(uint32_t num)
{
	if (num < 10) {
		bios_video_print_char(num + '0');
	} else {
		print_dec(num / 10);
		bios_video_print_char(num % 10 + '0');
	}
}

void printf(const char* format, ...)
{
	va_list args;
	va_start(args, format);
	while (*format != '\0')
	{
		if (*format == '%')
		{
			format++;
			switch (*format)
			{
			case 'c':
			{
				char c = va_arg(args, int);
				bios_video_print_char(c);
				break;
			}
			case 's':
			{
				char* str = va_arg(args, char *);
				print(str);
				break;
			}
			case 'd':
			{
				int32_t num = va_arg(args, int32_t);
				if (num < 0)
				{
					bios_video_print_char('-');
					num = -num;
				}
				print_dec(num);
				break;
			}
			case 'x':
			{
				uint32_t num = va_arg(args, uint32_t);
				print("0x");
				print_hex(num);
				break;
			}
			}
		}
		else
		{
			bios_video_print_char(*format);
		}
		format++;
	}

	va_end(args);

}

// This is very HW specific, need to adjust for your HW!
void bios_video_set_mode(uint8_t mode) {
	asm volatile("int $0x10" : : "a"(0x00), "b"(mode));
}

// Boot entry point!
void main()
{
	bios_video_set_mode(0x03);
	printf("Hello world!\n");
	printf("%d\n", -123);
	printf("%x\n", 0x12345678);
	for (;;)
	{}
}

Here is the complete source code

Categories
Crypto

TRON: ZombieSurvival

Now that we have successfully developed the popular Rock-Paper-Scissors game for the TRON blockchain, let’s take on a new challenge and create an action-packed game – Zombie Survival.

ZombieSurvival is a smart contract game built on the TRON blockchain using the Solidity programming language. The game simulates a zombie apocalypse where each player is a survivor, and they can join the game, shoot zombies, acquire score, and acquire items and weapons to improve their chances of survival. Each player has an owner, health, ammo, and score. The contract has several functions that allow players to join the game, shoot zombies, acquire items, upgrade weapons, complete quests and more.

When a player joins the game, they are added to the mapping “players” as a struct Player with a default health, ammo and score. The player can then use the “shoot” function to try and hit the zombies, this function will check if the player’s health and ammo are greater than 0 and it will use the function “checkHit” to determine if the player hit the zombies or not. If the player hit the zombies, the player’s health will decrease by 10, if the player’s health reaches 0, the player’s stats will be reset.

The contract also has functions to acquire items, upgrade weapons, complete quests and trade with other players. The player can use the “acquireItem” function to purchase items using their score. The player can use the “upgradeWeapon” function to upgrade their weapon using their score. The player can use the “completeQuest” function to complete quests and earn rewards. The player can use the “trade” function to trade with other players using items.

The contract also emits events when a player shoots, when a player’s health changes and when a player’s score changes.

pragma solidity ^0.4.24;

contract ZombieSurvival {
    //Struct to hold the information of a player
    struct Player {
        address owner;
        uint health;
        uint ammo;
        uint score;
        mapping(string => uint) inventory;
        mapping(string => uint) upgrades;
    }

    //Mapping to hold all the players
    mapping(address => Player) public players;

    //Event to be emitted when player shoots a zombies
    event Shoot(address shooter);
    //Event to be emitted when player's health changes
    event HealthChanged(address player, uint newHealth);
    //Event to be emitted when player's score changes
    event ScoreChanged(address player, uint newScore);

    //Function to join the game
    function joinGame() public {
        require(players[msg.sender].owner == address(0));
        players[msg.sender].owner = msg.sender;
        players[msg.sender].health = 100;
        players[msg.sender].ammo = 20;
        players[msg.sender].score = 0;
    }

    //Function to shoot a zombies
    function shoot() public {
        require(players[msg.sender].health > 0);
        require(players[msg.sender].ammo > 0);
        //decrement ammo
        players[msg.sender].ammo--;
        //increment score
        players[msg.sender].score++;
        //check if the player hit a zombies
        if (checkHit()) {
            //decrement health
            players[msg.sender].health -= 10;
            emit HealthChanged(msg.sender, players[msg.sender].health);
            checkDeath();
        }
        emit ScoreChanged(msg.sender, players[msg.sender].score);
        emit Shoot(msg.sender);
    }

    //Function to acquire an item
    function acquireItem(string _itemName) public {
        require(players[msg.sender].score >= getItemCost(_itemName));
        require(players[msg.sender].inventory[_itemName] == 0);
        players[msg.sender].score -= getItemCost(_itemName);
        players[msg.sender].inventory[_itemName]++;
    }
    //Function to upgrade a weapon
    function upgradeWeapon(string _weaponName) public {
        require(players[msg.sender].score >= getWeaponUpgradeCost(_weaponName));
        uint currentLevel = getWeaponLevel(msg.sender, _weaponName);
        require(currentLevel != 0);
        require(currentLevel < maxWeaponLevel);
        players[msg.sender].score -= getWeaponUpgradeCost(_weaponName);
        players[msg.sender].upgrades[_weaponName]++;
    }
    //Function to complete a quest
    function completeQuest(string _questName) public {
        require(players[msg.sender].health > 0);
        require(players[msg.sender].ammo > 0);
        //check if the player completed the quest
        if (checkQuestCompletion(_questName)) {
            //increment score
            players[msg.sender].score += getQuestReward(_questName);
            //increment health
            players[msg.sender].health += getQuestHealthReward(_questName);
            //increment ammo
            players[msg.sender].ammo += getQuestAmmoReward(_questName);
        }
    }
    //Function to buy TRX from the shop
    function buyTRX(uint _amount) public {
        require(msg.sender.transfer(_amount));
    }
    //Function to sell TRX to the shop
    function sellTRX(uint _amount) public payable {
        require(msg.value == _amount);
    }
    //Function to check if the player completed a quest
    function checkQuestCompletion(string _questName) private view returns (bool) {
        uint random = uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty))) % 2;
        if (random == 1) return true;
        else return false;
    }
    //Function to get the cost of an item
    function getItemCost(string _itemName) public view returns (uint) {
        if (_itemName == "medkit") return 1000;
        if (_itemName == "ammo_box") return 5000;
        if (_itemName == "first_aid_kit") return 10000;
    }
    //Function to get the cost of upgrading a weapon
    function getWeaponUpgradeCost(string _weaponName) public view returns (uint) {
        if (_weaponName == "pistol") return 500;
        if (_weaponName == "shotgun") return 2500;
        if (_weaponName == "rifle") return 5000;
    }
    //Function to get the reward of a quest
    function getQuestReward(string _questName) public view returns (uint) {
        if (_questName == "survival") return 100;
        if (_questName == "zombie_hunt") return 250;
        if (_questName == "rescue_mission") return 500;
    }
    //Function to get the health reward of a quest
    function getQuestHealthReward(string _questName) public view returns (uint) {
        if (_questName == "survival") return 10;
        if (_questName == "zombie_hunt") return 25;
        if (_questName == "rescue_mission") return 50;
    }
    //Function to get the ammo reward of a quest
    function getQuestAmmoReward(string _questName) public view returns (uint) {
        if (_questName == "survival") return 5;
        if (_questName == "zombie_hunt") return 10;
        if (_questName == "rescue_mission") return 20;
    }
    //Function to get the current level of a weapon
    function getWeaponLevel(address _owner, string _weaponName) public view returns (uint) {
        return players[_owner].upgrades[_weaponName];
    }
    //Function to login and get a daily reward
    function login() public {
        require(players[msg.sender].health > 0);
        require(players[msg.sender].ammo > 0);
        //increment score
        players[msg.sender].score += 10;
        //increment health
        players[msg.sender].health += 5;
        //increment ammo
        players[msg.sender].ammo += 2;
    }
    //Function to save the game progress
    function saveProgress() public {
        //Save the player's data to storage or IPFS
    }
    //Function to load the game progress
    function loadProgress() public view returns (bool) {
        //Load the player's data from storage or IPFS
        //if the data is successfully loaded return true, else return false
    }
    //Function to trade with another player
    function trade(address _otherPlayer, string _itemName, uint _amount) public {
        require(players[msg.sender].inventory[_itemName] >= _amount);
        require(players[_otherPlayer].inventory[_itemName] >= _amount);
        players[msg.sender].inventory[_itemName] -= _amount;
        players[_otherPlayer].inventory[_itemName] += _amount;
    }
}

Full source code is here.

Categories
Crypto

TRON: Kingdoms

Let’s build a game using TRON, and what better game to create than rock-paper-scissors? We’ve already covered TRON, so now it’s time to put that knowledge into practice.

pragma solidity ^0.4.24;

contract Kingdoms {
    // Struct to hold the information of a Kingdom
    struct Kingdom {
        address owner;
        uint army;
        uint gold;
        uint resources;
        mapping(string => uint) structures;
    }

    // Mapping to hold all the Kingdoms
    mapping(address => Kingdom) public kingdoms;

    // Event to be emitted on attack
    event Attack(address attacker, address defender, bool success);
    //Event to be emitted on structure building
    event Build(address owner, string structureName);

    // Function to create a new Kingdom
    function createKingdom(uint initialArmy, uint initialGold, uint initialResources) public {
        require(kingdoms[msg.sender].owner == address(0));
        kingdoms[msg.sender].owner = msg.sender;
        kingdoms[msg.sender].army = initialArmy;
        kingdoms[msg.sender].gold = initialGold;
        kingdoms[msg.sender].resources = initialResources;
    }

    // Function to attack another Kingdom
    function attack(address _defender) public {
        require(kingdoms[msg.sender].army > 0);
        require(kingdoms[_defender].owner != address(0));
        //calculate the success of the attack
        bool success = calculateAttack(kingdoms[msg.sender].army, kingdoms[_defender].army);
        if (success) {
            kingdoms[_defender].army = kingdoms[_defender].army / 2;
            kingdoms[msg.sender].gold += kingdoms[_defender].gold;
            kingdoms[msg.sender].resources += kingdoms[_defender].resources;
        } else {
            kingdoms[msg.sender].army = kingdoms[msg.sender].army / 2;
        }
        emit Attack(msg.sender, _defender, success);
    }
    // Function to build structures in a Kingdom
    function buildStructure(string _structureName) public {
        require(kingdoms[msg.sender].gold >= getStructureCost(_structureName));
        require(getStructureLevel(msg.sender, _structureName) == 0);
        kingdoms[msg.sender].gold -= getStructureCost(_structureName);
        kingdoms[msg.sender].structures[_structureName] = 1;
        emit Build(msg.sender, _structureName);
    }

    // Function to upgrade a structure in a Kingdom
    function upgradeStructure(string _structureName) public {
        require(kingdoms[msg.sender].gold >= getStructureUpgradeCost(_structureName));
        uint currentLevel = getStructureLevel(msg.sender, _structureName);
        require(currentLevel != 0);
        require(currentLevel < maxStructureLevel);
        kingdoms[msg.sender].gold -= getStructureUpgradeCost(_structureName);
        kingdoms[msg.sender].structures[_structureName]++;
    }

    // Function to get the current level of a structure in a Kingdom
    function getStructureLevel(address _owner, string _structureName) public view returns (uint) {
        return kingdoms[_owner].structures[_structureName];
    }

    // Function to get the cost of building a structure
    function getStructureCost(string _structureName) public view returns (uint) {
        if (_structureName == "wall") return 1000;
        if (_structureName == "tower") return 5000;
        if (_structureName == "barracks") return 10000;
    }

    // Function to get the cost of upgrading a structure
    function getStructureUpgradeCost(string _structureName) public view returns (uint) {
        if (_structureName == "wall") return 500;
        if (_structureName == "tower") return 2500;
        if (_structureName == "barracks") return 5000;
    }

    // Function to calculate the success of the attack
    function calculateAttack(uint attackerArmy, uint defenderArmy) private view returns (bool) {
        uint random = uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty))) % 2;
        if (random == 1 && attackerArmy > defenderArmy) return true;
        else return false;
    }
}

Full source code can be found here.

The smart contract allows players to create new kingdoms, attack other players, and acquire resources and gold and build structures.

The new feature allows players to build and upgrade structures in their kingdom. Structures can be walls, towers, or barracks. The function buildStructure allows a player to build a new structure, it checks if the player has enough gold and if the structure doesn’t exist in the player’s kingdom. When a player builds a new structure, the gold is subtracted from the player’s kingdom and the structure is added to the kingdom with level 1.

The function upgradeStructure allows players to upgrade an existing structure, it checks if the player has enough gold, if the structure exist and if the structure is not at the max level. Upgrading a structure will increase the level of the structure and the gold will be subtracted from the player’s kingdom.

The function getStructureLevel allows players to check the current level of a structure in their kingdom. The function getStructureCost and getStructureUpgradeCost allows players to check the cost of building or upgrading a structure.

The smart contract also includes events that are emitted when a player builds or upgrades a structure, the event includes the player’s address and the name of the structure.

Categories
Crypto

EOS: rock-paper-scissors

Let’s build a game using EOS, and what better game to create than rock-paper-scissors? We’ve already covered EOS in a previous example, so now it’s time to put that knowledge into practice.

// rock-paper-scissors game in EOS

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace eosio;

class rps : public contract {
 public:
	using contract::contract;

	[[eosio::action]]
	void play(name player1, name player2, std::string choice1, std::string choice2) {
		require_auth(player1);
		require_auth(player2);

		eosio_assert(choice1 == "rock" || choice1 == "paper" || choice1 == "scissors", "Invalid choice1");
		eosio_assert(choice2 == "rock" || choice2 == "paper" || choice2 == "scissors", "Invalid choice2");

		game_index games(_self, _self.value);
		games.emplace(player1, [&](auto& g) {
			g.key = games.available_primary_key();
			g.player1 = player1;
			g.player2 = player2;
			g.choice1 = choice1;
			g.choice2 = choice2;
		});
	}

	[[eosio::action]]
	void reveal(uint64_t key) {
		game_index games(_self, _self.value);
		auto itr = games.find(key);
		eosio_assert(itr != games.end(), "Game not found");

		name p1 = itr->player1;
		name p2 = itr->player2;
		require_auth(p1);
		require_auth(p2);

		endgame(itr);
	}

 private:
	struct [[eosio::table]] game {
		uint64_t key;
		name player1;
		name player2;
		std::string choice1;
		std::string choice2;

		uint64_t primary_key() const { return key; }
	};

	typedef eosio::multi_index<"games"_n, game> game_index;

	void endgame(game_index::const_iterator itr) {
		name winner;
		std::string choice1 = itr->choice1;
		std::string choice2 = itr->choice2;
		if (choice1 == choice2) {
			winner = "tie"_n;
		} else if (choice1 == "rock" && choice2 == "scissors" || choice1 == "paper" && choice2 == "rock" || choice1 == "scissors" && choice2 == "paper") {
			winner = itr->player1;
		} else {
			winner = itr->player2;
		}

		print("Winner: ", winner);
	}
};

EOSIO_DISPATCH(rps, (play)(reveal))

Full source code is here.

The contract allows players to play the game by specifying their choices of rock, paper, or scissors. The game is saved in a multi-index table, and the players can reveal the result by providing the game’s key. The contract includes two actions: play and reveal.

The play action is used to save the game with player1, player2, choice1, choice2.

The reveal action is used to reveal the game result, it requires the key of the game and the auth of player1 and player2.

The game struct is used to save the game details, it includes the primary key, player1, player2, choice1, and choice2.

The endgame function is used to reveal the game result and it takes a const reference to the game struct.

Categories
Crypto

EOS with examples

EOS is a blockchain platform that was created to support the development and execution of decentralized applications (dApps). It uses a Delegated Proof of Stake (DPoS) consensus algorithm, which allows for high scalability and fast transaction processing.

EOS has its own native cryptocurrency called EOS tokens, which are used to pay for resources such as network bandwidth and storage on the EOS blockchain. The EOS platform also includes a web assembly (WASM) virtual machine, which allows for the execution of smart contracts written in multiple programming languages.

One of the key features of EOS is its ability to handle a high number of transactions per second (TPS). While most blockchain platforms are limited to a few TPS, EOS is designed to handle millions of TPS. This is achieved through a combination of a highly efficient consensus algorithm and a unique system of resource allocation.

Another key feature of EOS is its governance structure. The EOS community elects 21 block producers (BPs) through a voting process, who are responsible for maintaining the network and producing new blocks. These BPs are incentivized to act in the best interest of the community, as they are rewarded with EOS tokens for their contributions to the network.

Smart Contracts

EOS smart contracts are written in web assembly (WASM) and can be written in multiple programming languages such as C++, Rust and others.

EOS smart contracts are similar to those on other blockchain platforms, such as Ethereum, in that they allow for the execution of complex logic and the creation of decentralized applications (dApps). However, EOS smart contracts have some unique features that set them apart from other platforms.

One of the key features of EOS smart contracts is their ability to use a “parallel processing” model. This means that multiple smart contracts can be executed simultaneously, which increases the overall speed and efficiency of the network.

Another key feature of EOS smart contracts is the ability to use a “multi-threading” model. This means that a single smart contract can be executed by multiple threads, which increases the overall performance and scalability of the network.

EOS smart contracts also have the ability to access and use the resources of the EOS blockchain, such as network bandwidth and storage. This is achieved through a unique system of resource allocation, where the developer must hold a certain amount of EOS tokens to access and use these resources.

There are also other features such as token creation, peer-to-peer transactions, and more…

Example: token.cpp

#include <eosio/eosio.hpp>

using namespace eosio;

CONTRACT token
: public contract {
public:
using contract::contract;

ACTION create(name issuer, asset maximum_supply);

ACTION issue(name to, asset quantity, string memo);

ACTION transfer(name from, name to, asset quantity, string memo);

private:
TABLE account{
	asset    balance;
	uint64_t primary_key()const { return balance.symbol.code().raw(); }
};

TABLE currency_stats{
	asset          supply;
	asset          max_supply;
	name           issuer;
	uint64_t primary_key()const { return supply.symbol.code().raw(); }
};

typedef eosio::multi_index<"accounts"_n, account> accounts;
typedef eosio::multi_index<"stat"_n, currency_stats> stats;

void sub_balance(name owner, asset value);

void add_balance(name owner, asset value, name ram_payer);

};

ACTION token::create(name issuer, asset maximum_supply)
{
	require_auth(issuer);
	auto sym = maximum_supply.symbol;
	eosio::check(sym.is_valid(), "invalid symbol name");
	eosio::check(maximum_supply.is_valid(), "invalid supply");
	eosio::check(maximum_supply.amount > 0, "max-supply must be positive");

	stats statstable(get_self(), sym.code().raw());
	auto existing = statstable.find(sym.code().raw());
	eosio::check(existing == statstable.end(), "token with symbol already exists");

	statstable.emplace(issuer, [&](auto& s)
	{
		s.supply = asset{ 0, sym };
		s.max_supply = maximum_supply;
		s.issuer = issuer;
	});
}

ACTION token::issue(name to, asset quantity, string memo)
{
	auto sym = quantity.symbol;
	eosio::check(sym.is_valid(), "invalid symbol name");
	eosio::check(memo.size() <= 256, "memo has more than 256 bytes");

	stats statstable(get_self(), sym.code().raw());
	auto existing = statstable.find(sym.code().raw());
	eosio::check(existing != statstable.end(), "token with symbol does not exist, create token before issue");
	const auto& st = *existing;

	require_auth(st.issuer);
	eosio::check(quantity.is_valid(), "invalid quantity");
	eosio::check(quantity.amount > 0, "must issue positive quantity");

	eosio::check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
	eosio::check(quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
	statstable.modify(st, same_payer, [&](auto& s)
	{
		s.supply += quantity;
	});

	add_balance(to, quantity, st.issuer);
}

ACTION token::transfer(name from, name to, asset quantity, string memo)
{
	eosio::check(from != to, "cannot transfer to self");
	require_auth(from);
	eosio::check(is_account(to), "to account does not exist");
	auto sym = quantity.symbol.code();
	stats statstable(get_self(), sym.raw());
	const auto& st = statstable.get(sym.raw());
	require_recipient(from);
	require_recipient(to);

	eosio::check(quantity.is_valid(), "invalid quantity");
	eosio::check(quantity.amount > 0, "must transfer positive quantity");
	eosio::check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
	eosio::check(memo.size() <= 256, "memo has more than 256 bytes");

	sub_balance(from, quantity);
	add_balance(to, quantity, from);
}

void token::sub_balance(name owner, asset value)
{
	accounts from_acnts(get_self(), owner.value);

	const auto& from = from_acnts.get(value.symbol.code().raw(), "no balance object found");
	eosio::check(from.balance.amount >= value.amount, "overdrawn balance");

	from_acnts.modify(from, owner, [&](auto& a)
	{
		a.balance -= value;
	});
}

void token::add_balance(name owner, asset value, name ram_payer)
{
	accounts to_acnts(get_self(), owner.value);
	auto to = to_acnts.find(value.symbol.code().raw());
	if (to == to_acnts.end())
	{
		to_acnts.emplace(ram_payer, [&](auto& a)
		{
			a.balance = value;
		});
	}
	else
	{
		to_acnts.modify(to, same_payer, [&](auto& a)
		{
			a.balance += value;
		});
	}
}

This contract is a template for creating, issuing, and transferring a custom ERC-20-like token on the EOS blockchain.

It inherits from the contract class provided by the EOSIO library, which allows it to interact with the EOS blockchain.

The contract defines three actions:

  • create: allows the issuer to create a new token with a specified maximum supply.
  • issue: allows the issuer to issue a certain amount of the token to a specified recipient.
  • transfer: allows a user to transfer a certain amount of the token to another user.

It also defines two tables:

  • account: stores the balance of each user for a specific token.
  • currency_stats: stores the overall supply, maximum supply, and issuer of the token.

And helper functions:

  • sub_balance: subtracts a certain amount of the token from a user’s balance.
  • add_balance: adds a certain amount of the token to a user’s balance.

Overall, this contract provides the basic functionality for creating and managing a custom token on the EOS blockchain, and can be adapted to the specific requirements of a given application.

Full source code here https://github.com/michaldrozd/eos-smart-contracts/blob/main/token_contract.cpp

Categories
Work hacks

Teams captions monitor

Do you want to get notified when someone mentions you in Microsoft Teams? Mainly when you can miss it if you focus on something else!

First, turn on live captions in MS Teams.

Then, just type the following in the Javascript console in your browser

// Select the div element to be monitored
// This div may change later, so use devtools inspector to find DIV which wrapps all captions
var div = document.querySelector('div[data-tid="closed-captions-renderer"]');

// Variable to keep track of the last time the alert was shown
var lastAlertTime = 0;

// Set an interval to check the contents of the div every 500 milliseconds
setInterval(function() {
    // Get the current time
    var currentTime = Date.now();
    
    // Get the contents of the div
    var divContent = div.textContent;
    
    // Check if the div contains either "Michal" or "Michael"
    if (divContent.includes("Michal") || divContent.includes("Michael")) {
        // Check if more than 5 seconds have passed since the last alert
        if (currentTime - lastAlertTime > 5000) {
            // If more than 5 seconds have passed, show the alert, or do whatever you like! But alert should make browser tab focused.
            alert("They are searching for you!");
            
            // Update the last alert time
            lastAlertTime = currentTime;
        }
    }
}, 500);

Then you get a browser alert when someone mentions your name (Michal or Michael)

Categories
Crypto

Cardano with examples

Cardano is a blockchain platform that uses a proof-of-stake consensus algorithm called Ouroboros. It is built using the Haskell programming language and is designed to be a more secure, flexible, and sustainable platform for the development and execution of smart contracts and decentralized applications (dapps).

One key feature of Cardano is its multi-layer architecture, which separates the settlement layer (where transactions are recorded) from the computation layer (where smart contracts are executed). This allows for greater flexibility and scalability, as changes to the computation layer can be made without affecting the settlement layer.

Another important aspect of Cardano is its use of formal verification, a method of mathematically proving the correctness of smart contract code. This helps to ensure the security and reliability of dapps built on the platform.

One example of a dapp built on Cardano is Plutus, a smart contract programming language that is specifically designed for the platform. Plutus allows for the creation of highly secure and reliable smart contracts, and also enables developers to write code that is more easily verifiable and less prone to errors.

Example: auction contract

Here is a practical smart contract in Plutus:

module Main where

import Language.PlutusTx
import Language.PlutusTx.Prelude
import Language.PlutusTx.Builtins

data Auction = Auction {
    itemName :: String,
    itemDescription :: String,
    startPrice :: Integer,
    highestBid :: Integer,
    highestBidder :: Address
}

placeBid : Auction -> Integer -> (Boolean, Auction)
placeBid auction bid =
    if bid > highestBid auction then
        (True, auction { highestBid = bid, highestBidder = txSender })
    else
        (False, auction)

endAuction : Auction -> (Boolean, String)
endAuction auction =
    if txSender == highestBidder auction then
        (True, "Auction sold to " ++ show (highestBidder auction) ++ " for " ++ show (highestBid auction))
    else
        (False, "Auction not ended")

main : () -> ()
main _ =
    let
        auction = Auction {
            itemName = "My Item",
            itemDescription = "This is a very nice item",
            startPrice = 100,
            highestBid = 0,
            highestBidder = Address "0x0"
        }
    in
    trace auction
    trace $ placeBid auction 150

This smart contract example is an auction contract, it defines a custom data type called “Auction” which has five fields, itemName, itemDescription, startPrice, highestBid, and highestBidder. It also defines two functions to interact with the auction, placeBid, endAuction.

The placeBid function allows a user to place a bid on an auction. It checks that the bid is greater than the current highest bid and if so, it updates the highest bid and highest bidder. It returns a tuple of Boolean and the updated auction. The boolean value is true if the bid is successfully placed, otherwise it is false.

The endAuction function allows the highest bidder to end the auction and it checks that the sender of the transaction is the highest bidder. If so, it returns a tuple of Boolean and a message indicating that the auction is sold to the highest bidder for the highest bid amount. Otherwise, it returns a tuple of Boolean and a message indicating that the auction is not ended.

The main function is the entry point of the contract, it creates a new auction, traces it, and traces the result of the placeBid function.

The source code is here https://github.com/michaldrozd/cardano-smart-contracts/blob/main/AuctionContract.plutus

Example: crowdfunding

Another example of a practical smart contract that implements a simple crowdfunding campaign written in Plutus for Cardano:

module Main where

import Language.PlutusTx
import Language.PlutusTx.Prelude
import Language.PlutusTx.Builtins

data Campaign = Campaign {
    campaignName :: String,
    campaignDescription :: String,
    targetFunding :: Integer,
    currentFunding :: Integer,
    deadline :: Integer
}

contribute : Campaign -> Integer -> (Boolean, Campaign)
contribute campaign amount =
    if (txTimestamp <= deadline campaign) && (currentFunding campaign + amount <= targetFunding campaign) then
        (True, campaign { currentFunding = currentFunding campaign + amount })
    else
        (False, campaign)

refund : Campaign -> Address -> (Boolean, Integer)
refund campaign contributor =
    if (txSender == contributor) && (currentFunding campaign < targetFunding campaign) then
        (True, currentFunding campaign)
    else
        (False, 0)

main : () -> ()
main _ =
    let
        campaign = Campaign {
            campaignName = "My Campaign",
            campaignDescription = "This is a very nice campaign",
            targetFunding = 1000,
            currentFunding = 0,
            deadline = 10000000000
        }
    in
    trace campaign
    trace $ contribute campaign 500

This smart contract defines a custom data type called “Campaign” which has five fields, campaignName, campaignDescription, targetFunding, currentFunding, and deadline. It also defines two functions to interact with the campaign, contribute, refund.

The contribute function allows a user to contribute to a campaign. It checks that the transaction timestamp is less than or equal to the campaign deadline and that the total funding after the contribution is less than or equal to the target funding. If the checks pass, it updates the current funding and returns a tuple of Boolean and the updated campaign. The boolean value is true if the contribution is successfully placed, otherwise it is false.

The refund function allows a contributor to get a refund if the campaign is not fully funded. It checks that the sender of the transaction is the contributor and that the current funding is less than the target funding. If the checks pass, it returns a tuple of Boolean and the current funding. Otherwise, it returns a tuple of Boolean and 0.

The main function is the entry point of the contract, it creates a new campaign, traces it, and traces the result of the contribute function.

The source code is here https://github.com/michaldrozd/cardano-smart-contracts/blob/main/CrowdfundingCampaign.plutus

Example: Escrow

module Main where

import Language.PlutusTx
import Language.PlutusTx.Prelude
import Language.PlutusTx.Builtins

data Escrow = Escrow {
    buyer :: Address,
    seller :: Address,
    arbitrator :: Address,
    item :: String,
    amount :: Integer,
    status :: String
}

create : Address -> Address -> Address -> String -> Integer -> Escrow
create buyer seller arbitrator item amount =
    Escrow {
        buyer = buyer,
        seller = seller,
        arbitrator = arbitrator,
        item = item,
        amount = amount,
        status = "created"
    }

release : Escrow -> (Boolean, Escrow)
release escrow =
    if (txSender == buyer escrow) && (status escrow == "created") then
        (True, escrow { status = "released" })
    else
        (False, escrow)

refund : Escrow -> (Boolean, Escrow)
refund escrow =
    if (txSender == seller escrow) && (status escrow == "created") then
        (True, escrow { status = "refunded" })
    else
        (False, escrow)

arbitrate : Escrow -> (Boolean, Escrow)
arbitrate escrow =
    if (txSender == arbitrator escrow) && (status escrow == "created") then
        (True, escrow { status = "arbitrated" })
    else
        (False, escrow)

main : () -> ()
main _ =
    let
        escrow = create (Address "0x1") (Address "0x2") (Address "0x3") "My Item" 500
    in
    trace escrow
    trace $ release escrow

This smart contract example is an escrow service, it defines a custom data type called “Escrow” which has six fields, buyer, seller, arbitrator, item, amount and status. It also defines three functions to interact with the escrow create, release, refund and arbitrate.

The create function allows a user to create a new escrow transaction, it takes the buyer, seller and arbitrator addresses, an item name and the amount of the transaction, it creates an escrow object with the given data.

The release function allows the buyer to release the funds to the seller after they have received the item. It checks that the sender of the transaction is the buyer and that the status of the escrow is “created”, if so it updates the status of the escrow to “released” and returns a tuple of Boolean and the updated escrow. The boolean value is true if the release is successful, otherwise it is false.

The refund function allows the seller to refund the funds to the buyer if they have not received the item. It checks that the sender of the transaction is the seller and that the status of the escrow is “created”, if so it updates the status of the escrow to “refunded” and returns a tuple of Boolean and the updated escrow. The boolean value is true if the refund is successful, otherwise it is false.

The arbitrate function allows the arbitrator to resolve the escrow if there is a dispute between the buyer and the seller. It checks that the sender of the transaction is the arbitrator and that the status of the escrow is “created”, if so it updates the status of the escrow to “arbitrated” and returns a tuple of Boolean and the updated escrow. The boolean value is true if the arbitration is successful, otherwise it is false.

The main function is the entry point of the contract, it creates a new escrow, traces it, and traces the result of the release function.

Here is full source code: https://github.com/michaldrozd/cardano-smart-contracts/blob/main/EscrowService.plutus

Other real-world examples

Another example of a dapp built on cardano is Atala PRISM, which is a decentralized identity solution that allows individuals and organizations to have full control over their own digital identities.

A practical use case for Cardano is in the field of voting systems. A company called Agora is building a decentralized voting platform on Cardano that uses blockchain technology to ensure the security and transparency of the voting process. The platform allows for remote voting and eliminates the need for paper ballots, making the voting process more efficient and accessible.

Another example is in the field of charity and donation, a company called Giveth is building a platform that allows individuals to donate to charity organizations in a transparent and efficient way, by using smart contract to make sure the donation reach its intended target.

In summary, Cardano is a blockchain platform that uses a proof-of-stake consensus algorithm, is built using the Haskell programming language and designed to be more secure, flexible, and sustainable platform for the development and execution of smart contracts and decentralized applications (dapps). Its multi-layer architecture separates the settlement layer from the computation layer and uses formal verification as a method of mathematically proving the correctness of smart contract code.

Categories
Development

Full Stack – Example

Let’s put it all together and create simple full-stack application using Angular, Spring Boot, and Kubernetes

First, let’s create the Angular frontend. You can use the Angular CLI to create a new project:

ng new my-app

Once the project is created, you can build the frontend and create a production build:

ng build --prod

This will generate the production build of the frontend in the dist folder.

Next, let’s create the Spring Boot backend. You can use the Spring Initializer to create a new project:

curl https://start.spring.io/starter.zip -o my-app.zip

Once the project is created, you can add the necessary dependencies and code to create a REST API.

Now, you can package the Spring Boot application into a JAR file:

./mvnw clean package

With both the frontend and backend ready, you can create a Docker image for each.

Create a Dockerfile for the frontend:

FROM nginx:alpine
COPY dist/my-app /usr/share/nginx/html

And a Dockerfile for the backend:

FROM openjdk:8-jdk-alpine
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

You can then build the Docker images by running the command:

docker build -t my-app-frontend -f frontend.Dockerfile .
docker build -t my-app-backend -f backend.Dockerfile .

With the images built, you can now create a Kubernetes Deployment to manage the desired state of your application:

Deployment for frontend

my-app-frontend-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app-frontend
  template:
    metadata:
      labels:
        app: my-app-frontend
    spec:
      containers:
      - name: my-app-frontend
        image: my-app-frontend
        ports:
        - containerPort: 80

my-app-backend-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app-backend
  template:
    metadata:
      labels:
        app: my-app-backend
    spec:
      containers:
      - name: my-app-backend
        image: my-app-backend
        ports:
        - containerPort: 8080

And you can create a Kubernetes Service to expose the application to the outside world:

my-app-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: my-app-frontend
spec:
  selector:
    app: my-app-backend
    ports:
      name: http
      port: 8080
      targetPort: 8080
      type: ClusterIP

This is just a high-level example, in a real-world scenario, you might want to use Kubernetes volumes to manage the data, add environment variables or configmaps to configure the application, or use Kubernetes autoscaling feature.

Additionally, you will also want to use a Kubernetes ingress to route the traffic to the correct service based on the URL path. This will allow your application to handle different routes and endpoints for the frontend and backend.

Finally, you can deploy the application to a Kubernetes cluster by applying the above configuration files using the kubectl command:

kubectl apply -f my-app-frontend-deployment.yaml
kubectl apply -f my-app-backend-deployment.yaml
kubectl apply -f my-app-service.yaml

That’s it! Your application is now running on a Kubernetes cluster and can be accessed through the external IP address of the frontend service.

Keep in mind that this is a simplified example and in a real-world scenario, there might be more complexity and additional steps required.