Sep 22, 2023

Bitcoin Halving

 Bitcoin halving is a significant event on the Bitcoin network every four years. During this event, the block reward that miners receive for verifying transactions and adding new blocks to the blockchain is reduced by 50%. This means that the rate of new Bitcoin creation slows down, and the total supply of Bitcoin approaches its maximum limit of 21 million.


Bitcoin halving is a programmed event and is built into the Bitcoin protocol to ensure that the inflation rate of Bitcoin remains controlled and predictable. The reduced rate of new Bitcoin creation and the expectation of scarcity can increase the value of Bitcoin, which has historically led to an increase in the asset's price in the months leading up to a halving event.
Despite this, the market can be unpredictable, and the impact of halving Bitcoin's price is not guaranteed. However, the reduced supply of Bitcoin resulting from halving helps to maintain its value and ensure that it remains a finite and scarce asset.

The previous Bitcoin halving occurred on May 11, 2020, at a block height of 630,000. At that time, the block reward for miners was reduced from 12.5 BTC to 6.25 BTC per block. This was the third halving event in Bitcoin's history, following the first halving in November 2012 and the second halving in July 2016. The next Bitcoin halving is expected to occur in march 2024, at which point the block reward will be reduced from 6.25 BTC to 3.125 BTC per block.

After the first Bitcoin halving in November 2012, the price of Bitcoin increased by over 8,000% over the following year. After the second halving in July 2016, the price of Bitcoin increased by around 2,500% over the following 18 months. After the most recent halving event in May 2020, the price of Bitcoin initially experienced a slight drop but quickly recovered and went on to gain over 300% in value over the following year, reaching an all-time high of over $64,000 in April 2021.

Sep 7, 2023

Top 5 AWS Interview Tips

 

  • Can you describe the current AWS infrastructure and technologies used within the company?
    • This question shows your eagerness to understand the existing AWS setup and gives you insights into the company's tech stack.
  • What AWS services or tools are particularly important for this role, and how are they utilized here?
    • This question allows you to gauge the specific responsibilities of the role and how AWS is integrated into the company's operations.
  • How does the company handle AWS security and compliance, especially in relation to [mention relevant industry standards or regulations]?
    • Demonstrates your concern for security and regulatory compliance, which is crucial in many AWS roles.
  • What are the biggest challenges or projects related to AWS that the team is currently working on or will be working on in the near future?
    • Shows your interest in contributing to the team's objectives and your willingness to tackle challenges.

Sep 6, 2023

Database vs. Data Warehouse vs. Data Lake

In the world of data management, these three concepts play distinct roles in handling and organizing data. Let's explore their key differences:


📊 Database:
A database is a structured collection of data that is organized, stored, and managed using a predefined schema. It's designed for efficient data retrieval and modification. Databases are used to support transactional operations(OLTP), such as recording customer orders, tracking inventory, and managing user profiles.

🏢 Data Warehouse:
A data warehouse is a centralized repository that aggregates data from various sources across an organization. It's optimized for complex queries and data analysis(OLAP). Data warehouses often use a process called Extract, Transform, Load (ETL) to integrate data from different systems, transform it into a consistent format, and load it into the warehouse. They are used for business intelligence, reporting, and decision-making.

🌊 Data Lake:
A data lake is a vast storage repository that holds both structured and unstructured data at any scale. Unlike traditional databases, data lakes don't require a predefined schema. This flexibility allows organizations to store raw, unprocessed data from various sources, making it suitable for advanced analytics, machine learning, and exploration of new data sources(ELT)

💡 Key Takeaways:
Use Cases: Databases are used for day-to-day operations, data warehouses are for business intelligence, and data lakes are for storing and analyzing large volumes of diverse data.

Schema: Databases have a fixed schema, data warehouses have a structured schema for reporting, and data lakes allow schema-on-read for flexibility.

Data Types: Databases store structured data, data warehouses store structured and semi-structured data, and data lakes store structured, semi-structured, and unstructured data.

May 26, 2023

Solidity - Block Chain 3

 Internal and External

In addition to public and private, Solidity has two more types of visibility for functions: internal and external.

internal is the same as private, except that it's also accessible to contracts that inherit from this contract. (Hey, that sounds like what we want here!).

external is similar to public, except that these functions can ONLY be called outside the contract — they can't be called by other functions inside that contract. We'll talk about why you might want to use external vs public later.

For declaring internal or external functions, the syntax is the same as private and public:

contract Sandwich {
  uint private sandwichesEaten = 0;

  function eat() internal {
    sandwichesEaten++;
  }
}

contract BLT is Sandwich {
  uint private baconSandwichesEaten = 0;

  function eatWithBacon() public returns (string memory) {
    baconSandwichesEaten++;
    // We can call this here because it's internal
    eat();
  }
}Interacting with other contracts

For our contract to talk to another contract on the blockchain that we don't own, first we need to define an interface.

Let's look at a simple example. Say there was a contract on the blockchain that looked like this:

contract LuckyNumber {
  mapping(address => uint) numbers;

  function setNum(uint _num) public {
    numbers[msg.sender] = _num;
  }

  function getNum(address _myAddress) public view returns (uint) {
    return numbers[_myAddress];
  }
}

This would be a simple contract where anyone could store their lucky number, and it will be associated with their Ethereum address. Then anyone else could look up that person's lucky number using their address.

Now let's say we had an external contract that wanted to read the data in this contract using the getNum function.

First we'd have to define an interface of the LuckyNumber contract:

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

Notice that this looks like defining a contract, with a few differences. For one, we're only declaring the functions we want to interact with — in this case getNum — and we don't mention any of the other functions or state variables.

Secondly, we're not defining the function bodies. Instead of curly braces ({ and }), we're simply ending the function declaration with a semi-colon (;).

So it kind of looks like a contract skeleton. This is how the compiler knows it's an interface.

By including this interface in our dapp's code our contract knows what the other contract's functions look like, how to call them, and what sort of response to expect.

Using an Interface

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

We can use it in a contract as follows:

contract MyContract {
  address NumberInterfaceAddress = 0xab38...
  // ^ The address of the FavoriteNumber contract on Ethereum
  NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
  // Now `numberContract` is pointing to the other contract

  function someFunction() public {
    // Now we can call `getNum` from that contract:
    uint num = numberContract.getNum(msg.sender);
    // ...and do something with `num` here
  }
}

In this way, your contract can interact with any other contract on the Ethereum blockchain, as long they expose those functions as public or external.

Handling Multiple Return Values


function multipleReturns() internal returns(uint a, uint b, uint c) {
  return (1, 2, 3);
}

function processMultipleReturns() external {
  uint a;
  uint b;
  uint c;
  // This is how you do multiple assignment:
  (a, b, c) = multipleReturns();
}

// Or if we only cared about one of the values:
function getLastReturnValue() external {
  uint c;
  // We can just leave the other fields blank:
  (,,c) = multipleReturns();
}

If statements

If statements in Solidity look just like JavaScript:

function eatBLT(string memory sandwich) public {
  // Remember with strings, we have to compare their keccak256 hashes
  // to check equality
  if (keccak256(abi.encodePacked(sandwich)) == keccak256(abi.encodePacked("BLT"))) {
    eat();
  }
}

Immutability of Contracts

 Solidity has looked quite similar to other languages like JavaScript. But there are a number of ways that Ethereum DApps are actually quite different from normal applications.

To start with, after you deploy a contract to Ethereum, it’s immutable, which means that it can never be modified or updated again.

The initial code you deploy to a contract is there to stay, permanently, on the blockchain. This is one reason security is such a huge concern in Solidity. If there's a flaw in your contract code, there's no way for you to patch it later. You would have to tell your users to start using a different smart contract address that has the fix.

But this is also a feature of smart contracts. The code is law. If you read the code of a smart contract and verify it, you can be sure that every time you call a function it's going to do exactly what the code says it will do. No one can later change that function and give you unexpected results.

External dependencies

we hard-coded the CryptoKitties contract address into our DApp. But what would happen if the CryptoKitties contract had a bug and someone destroyed all the kitties?

It's unlikely, but if this did happen it would render our DApp completely useless — our DApp would point to a hardcoded address that no longer returned any kitties. Our zombies would be unable to feed on kitties, and we'd be unable to modify our contract to fix it.

For this reason, it often makes sense to have functions that will allow you to update key portions of the DApp.

For example, instead of hard coding the CryptoKitties contract address into our DApp, we should probably have a setKittyContractAddress function that lets us change this address in the future in case something happens to the CryptoKitties contract.

Ownable Contracts

setKittyContractAddress is external, so anyone can call it! That means anyone who called the function could change the address of the CryptoKitties contract, and break our app for all its users.

We do want the ability to update this address in our contract, but we don't want everyone to be able to update it.

To handle cases like this, one common practice that has emerged is to make contracts Ownable — meaning they have an owner (you) who has special privileges.

OpenZeppelin's Ownable contract

Below is the Ownable contract taken from the OpenZeppelin Solidity library. OpenZeppelin is a library of secure and community-vetted smart contracts that you can use in your own DApps. After this lesson, we highly recommend you check out their site to further your learning!

Give the contract below a read-through. You're going to see a few things we haven't learned yet, but don't worry, we'll talk about them afterward.

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address private _owner;

  event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
  );

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() internal {
    _owner = msg.sender;
    emit OwnershipTransferred(address(0), _owner);
  }

  /**
   * @return the address of the owner.
   */
  function owner() public view returns(address) {
    return _owner;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(isOwner());
    _;
  }

  /**
   * @return true if `msg.sender` is the owner of the contract.
   */
  function isOwner() public view returns(bool) {
    return msg.sender == _owner;
  }

  /**
   * @dev Allows the current owner to relinquish control of the contract.
   * @notice Renouncing to ownership will leave the contract without an owner.
   * It will not be possible to call the functions with the `onlyOwner`
   * modifier anymore.
   */
  function renounceOwnership() public onlyOwner {
    emit OwnershipTransferred(_owner, address(0));
    _owner = address(0);
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    _transferOwnership(newOwner);
  }

  /**
   * @dev Transfers control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function _transferOwnership(address newOwner) internal {
    require(newOwner != address(0));
    emit OwnershipTransferred(_owner, newOwner);
    _owner = newOwner;
  }
}

A few new things here we haven't seen before:

  • Constructors: constructor() is a constructor, which is an optional special function. It will get executed only one time, when the contract is first created.
  • Function Modifiers: modifier onlyOwner(). Modifiers are kind of half-functions that are used to modify other functions, usually to check some requirements prior to execution. In this case, onlyOwner can be used to limit access so only the owner of the contract can run this function. We'll talk more about function modifiers in the next chapter, and what that weird _; does.
  • indexed keyword: don't worry about this one, we don't need it yet.

So the Ownable contract basically does the following:

  1. When a contract is created, its constructor sets the owner to msg.sender (the person who deployed it)

  2. It adds an onlyOwner modifier, which can restrict access to certain functions to only the owner

  3. It allows you to transfer the contract to a new owner

onlyOwner is such a common requirement for contracts that most Solidity DApps start with a copy/paste of this Ownable contract, and then their first contract inherits from it.

Since we want to limit setKittyContractAddress to onlyOwner, we're going to do the same for our contract.

Solidity - Block Chain 2

 Ethereum has the hash function keccak256 built in, which is a version of SHA3. A hash function basically maps an input into a random 256-bit hexadecimal number. A slight change in the input will cause a large change in the hash.

It's useful for many purposes in Ethereum, but for right now we're just going to use it for pseudo-random number generation.

Also important, keccak256 expects a single parameter of type bytes. This means that we have to "pack" any parameters before calling keccak256:

Example:

//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256(abi.encodePacked("aaaab"));
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256(abi.encodePacked("aaaac"));

As you can see, the returned values are totally different despite only a 1 character change in the input.

Typecasting

Sometimes you need to convert between data types. Take the following example:

uint8 a = 5;
uint b = 6;
// throws an error because a * b returns a uint, not uint8:
uint8 c = a * b;
// we have to typecast b as a uint8 to make it work:
uint8 c = a * uint8(b);

In the above, a * b returns a uint, but we were trying to store it as a uint8, which could cause potential problems. By casting it as a uint8, it works and the compiler won't throw an error.

Our contract is almost finished! Now let's add an event.

Events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen.

Example:

// declare the event
event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint _y) public returns (uint) {
  uint result = _x + _y;
  // fire an event to let the app know the function was called:
  emit IntegersAdded(_x, _y, result);
  return result;
}

Your app front-end could then listen for the event. A JavaScript implementation would look something like:

YourContract.IntegersAdded(function(error, result) {
  // do something with result
})Solidity contract is complete! Now we need to write a JavaScript frontend that interacts with the contract.

Ethereum has a JavaScript library called Web3.js.

Addresses

The Ethereum blockchain is made up of accounts, which you can think of like bank accounts. An account has a balance of Ether (the currency used on the Ethereum blockchain), and you can send and receive Ether payments to other accounts, just like your bank account can wire transfer money to other bank accounts.

Each account has an address, which you can think of like a bank account number. It's a unique identifier that points to that account, and it looks like this:

0x0cE446255506E92DF41614C46F1d6df9Cc969183

Understand that an address is owned by a specific user (or a smart contract).

So we can use it as a unique ID for ownership . When a user creates new entity/object by interacting with an app, will set ownership of those entity/object to the Ethereum address that called the function.

Mappings

Mappings are another way of storing organized data in Solidity.

Defining a mapping looks like this:

// For a financial app, storing a uint that holds the user's account balance:

mapping (address => uint) public accountBalance;

// Or could be used to store / lookup usernames based on userId

mapping (uint => string) userIdToName;

A mapping is essentially a key-value store for storing and looking up data. In the first example, the key is an address and the value is a uint, and in the second example the key is a uint and the value a string.

msg.sender

In Solidity, there are certain global variables that are available to all functions. One of these is msg.sender, which refers to the address of the person (or smart contract) who called the current function.

Note: In Solidity, function execution always needs to start with an external caller. A contract will just sit on the blockchain doing nothing until someone calls one of its functions. So there will always be a msg.sender.

Here's an example of using msg.sender and updating a mapping:

mapping (address => uint) favoriteNumber;

function setMyNumber(uint _myNumber) public {
  // Update our `favoriteNumber` mapping to store `_myNumber` under `msg.sender`
  favoriteNumber[msg.sender] = _myNumber;
  // ^ The syntax for storing data in a mapping is just like with arrays
}

function whatIsMyNumber() public view returns (uint) {
  // Retrieve the value stored in the sender's address
  // Will be `0` if the sender hasn't called `setMyNumber` yet
  return favoriteNumber[msg.sender];
}

In this trivial example, anyone could call setMyNumber and store a uint in our contract, which would be tied to their address. Then when they called whatIsMyNumber, they would be returned the uint that they stored.

Using msg.sender gives you the security of the Ethereum blockchain — the only way someone can modify someone else's data would be to steal the private key associated with their Ethereum address.

Require

How can we make  function can only be called once per player?

For that we use requirerequire makes it so that the function will throw an error and stop executing if some condition is not true:

function sayHiToVitalik(string memory _name) public returns (string memory) {
  // Compares if _name equals "Vitalik". Throws an error and exits if not true.
  // (Side note: Solidity doesn't have native string comparison, so we
  // compare their keccak256 hashes to see if the strings are equal)
  require(keccak256(abi.encodePacked(_name)) == keccak256(abi.encodePacked("Vitalik")));
  // If it's true, proceed with the function:
  return "Hi!";
}

If you call this function with sayHiToVitalik("Vitalik"), it will return "Hi!". If you call it with any other input, it will throw an error and not execute.

Thus require is quite useful for verifying certain conditions that must be true before running a function.

One feature of Solidity that makes this more manageable is contract inheritance:

contract Doge {
  function catchphrase() public returns (string memory) {
    return "So Wow CryptoDoge";
  }
}

contract BabyDoge is Doge {
  function anotherCatchphrase() public returns (string memory) {
    return "Such Moon BabyDoge";
  }
}

BabyDoge inherits from Doge. That means if you compile and deploy BabyDoge, it will have access to both catchphrase() and anotherCatchphrase() (and any other public functions we may define on Doge).

This can be used for logical inheritance (such as with a subclass, a Cat is an Animal). But it can also be used simply for organizing your code by grouping similar logic together into different contracts.

Import

When you have multiple files and you want to import one file into another, Solidity uses the import keyword:

import "./someothercontract.sol";

contract newContract is SomeOtherContract {

}

So if we had a file named someothercontract.sol in the same directory as this contract (that's what the ./ means), it would get imported by the compiler.

Storage vs Memory (Data location)

In Solidity, there are two locations you can store variables — in storage and in memory.

Storage refers to variables stored permanently on the blockchain. Memory variables are temporary, and are erased between external function calls to your contract. Think of it like your computer's hard disk vs RAM.

Most of the time you don't need to use these keywords because Solidity handles them by default. State variables (variables declared outside of functions) are by default storage and written permanently to the blockchain, while variables declared inside functions are memory and will disappear when the function call ends.

However, there are times when you do need to use these keywords, namely when dealing with structs and arrays within functions:

contract SandwichFactory {
  struct Sandwich {
    string name;
    string status;
  }

  Sandwich[] sandwiches;

  function eatSandwich(uint _index) public {
    // Sandwich mySandwich = sandwiches[_index];

    // ^ Seems pretty straightforward, but solidity will give you a warning
    // telling you that you should explicitly declare `storage` or `memory` here.

    // So instead, you should declare with the `storage` keyword, like:
    Sandwich storage mySandwich = sandwiches[_index];
    // ...in which case `mySandwich` is a pointer to `sandwiches[_index]`
    // in storage, and...
    mySandwich.status = "Eaten!";
    // ...this will permanently change `sandwiches[_index]` on the blockchain.

    // If you just want a copy, you can use `memory`:
    Sandwich memory anotherSandwich = sandwiches[_index + 1];
    // ...in which case `anotherSandwich` will simply be a copy of the
    // data in memory, and...
    anotherSandwich.status = "Eaten!";
    // ...will just modify the temporary variable and have no effect
    // on `sandwiches[_index + 1]`. But you can do this:
    sandwiches[_index + 1] = anotherSandwich;
    // ...if you want to copy the changes back into blockchain storage.
  }
}