Trong dự án asp net core, khi các bạn sử dụng Generic Repository Pattern, làm cách nào để register generic interface và implementation generic interface bằng cách sử dụng ASP.NET Core DI container?

Ok, với một interface đơn giản và implementation của nó, ta sử dụng như sau

serviceCollection.AddSingleton<IService, MyService>();

Vậy còn kiểu Generic thì sao?

Chúng ta cùng xem lại phát biểu về phương thức AddSingleton

public static IServiceCollection AddSingleton<TService, TImplementation>(this IServiceCollection serviceswhere TService : class where TImplementation : class, Tservice

Phương pháp này sử dụng được với hầu hết các trường hợp, ngoại trừ các service generic..

Xét một ví dụ đơn giản, chúng ta có interface

public interface IThing<T>
{
    string GetName { get; }
}

và một implementation của nó như sau

public class GenericThing<T> : IThing<T>
{
    public GenericThing()
    {
        GetName = typeof(T).Name;
    }

    public string GetName { get; }
}

Câu hỏi đặt ra là khi sử dụng IThing<SomeType> làm sao chúng ta lấy được chính xác GenericThing<SomeType> đã được injected vào?

Trong trường hợp này chúng ta sử dụng phương thức mở rộng khác trong ServiceCollection, bằng cách như sau

serviceCollection.AddSingleton(typeof(IThing<>), typeof(GenericThing<>));

Ok, giờ chúng ta có thể sử dụng DI bất cứ đâu cần Injected

public class ValuesController : Controller
{
    private readonly IThing<ValuesController> _thing;

    public ValuesController(IThing<ValuesController> thing)
    {
        _thing = thing;
    }
}

Trông cũng dễ hiểu phải không 🙂

Hãy comment đặt câu hỏi nếu các bạn cần trao đổi thêm về asp net core hoặc DI nhé!

Here I’ll show you how to get SQL Server 2017 up and running on your Mac in less than half an hour. And the best part is, you’ll have SQL Server running locally without needing any virtualization software.

Prior to SQL Server 2017, if you wanted to run SQL Server on your Mac, you first had to create a virtual machine (using VirtualBox, Parallels Desktop, VMware Fusion, or Bootcamp), then install Windows onto that VM, then finally SQL Server. This is still a valid option depending on your requirements (here’s how to install SQL Server on a Mac with VirtualBox if you’d like to try that method).

Starting with SQL Server 2017, you can now install SQL Server directly on to a Linux machine. And because macOS is Unix based (and Linux is Unix based), you can run SQL Server for Linux on your Mac. The way to do this is to run SQL Server on Docker.

So let’s go ahead and install Docker. Then we’ll download and install SQL Server.

Install Docker
Download the (free) Docker Community Edition for Mac (unless you’ve already got it installed on your system). This will enable you to run SQL Server from within a Docker container.

To download, visit the Docker CE for Mac download page and click Get Docker.

To install, double-click on the .dmg file and then drag the Docker.app icon to your Application folder.

Screenshot of the Docker installation.
Docker installation on a Mac.

What is Docker?
Docker is a platform that enables software to run in its own isolated environment. SQL Server 2017 can be run on Docker in its own isolated container. Once Docker is installed, you simply download — or “pull” — the SQL Server on Linux Docker Image to your Mac, then run it as a Docker container. This container is an isolated envionment that contains everything SQL Server needs to run.

Launch Docker
Launch Docker the same way you’d launch any other application (eg, via the Applications folder, the Launchpad, etc).

When you open Docker, you might be prompted for your password so that Docker can install its networking components and links to the Docker apps. Go ahead and provide your password, as Docker needs this to run.

Screenshot of the password request dialog
The password request dialog

Increase the Memory
By default, Docker will have 2GB of memory allocated to it. SQL Server needs at least 3.25GB. To be safe, increase it to 4GB if you can.

To do this:

Select Preferences from the little Docker icon in the top menu
Slide the memory slider up to at least 4GB
Click Apply & Restart
Screenshot of selecting the Preferences
Selecting the preferences.

Screenshot of increasing the memory
Increasing the memory.

Download SQL Server
Now that Docker is installed and its memory has been increased, we can download and install SQL Server for Linux.

Open a Terminal window and run the following command.

docker pull microsoft/mssql-server-linux
This downloads the latest SQL Server for Linux Docker image to your computer.

Launch the Docker Image
Run the following command to launch an instance of the Docker image you just downloaded:

docker run -d –name sql_server_demo -e ‘ACCEPT_EULA=Y’ -e ‘SA_PASSWORD=reallyStrongPwd123’ -p 1433:1433 microsoft/mssql-server-linux
But of course, use your own name and password.

Here’s an explanation of the parameters:

-d
This optional parameter launches the Docker container in daemon mode. This means that it runs in the background and doesn’t need its own Terminal window open. You can omit this parameter to have the container run in its own Terminal window.
–name sql_server_demo
Another optional parameter. This parameter allows you to name the container. This can be handy when stopping and starting your container from the Terminal.
-e ‘ACCEPT_EULA=Y’
The Y shows that you agree with the EULA (End User Licence Agreement). This is required in order to have SQL Server for Linux run on your Mac.
-e ‘SA_PASSWORD=reallyStrongPwd123’
Required parameter that sets the sa database password.
-p 1433:1433
This maps the local port 1433 to port 1433 on the container. This is the default TCP port that SQL Server uses to listen for connections.
microsoft/mssql-server-linux
This tells Docker which image to use.
Password Strength
If you get the following error at this step, try again, but with a stronger password.

Microsoft(R) SQL Server(R) setup failed with error code 1. Please check the setup log in /var/opt/mssql/log for more information.
I received this error when using reallyStrongPwd as the password (but of course, it’s not a really strong password!). I was able to overcome this by adding some numbers to the end. However, if it wasn’t just a demo I’d definitely make it stronger than a few dictionary words and numbers.

Check the Docker container (optional)
You can type the following command to check that the Docker container is running.

docker ps
If it’s up and running, it should return something like this:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4e4aa21eb391 microsoft/mssql-server-linux “/bin/sh -c /opt/m…” 23 seconds ago Up 21 seconds 0.0.0.0:1433->1433/tcp sql_server_demo
Install sql-cli (unless already installed)
Run the following command to install the sql-cli command line tool. This tool allows you to run queries and other commands against your SQL Server instance.

npm install -g sql-cli
This assumes you have NodeJs installed. If you don’t, download it from Nodejs.org first. Installing NodeJs will automatically install npm which is what we use in this command to install sql-cli.

Permissions Error?
If you get an error, and part of it reads something like Please try running this command again as root/Administrator, try again, but this time prepend sudo to your command:

sudo npm install -g sql-cli
Connect to SQL Server
Now that sql-cli is installed, we can start working with SQL Server via the Terminal window on our Mac.

Connect to SQL Server using the mssql command, followed by the username and password parameters.

mssql -u sa -p reallyStrongPwd123
You should see something like this:

Connecting to localhost…done

sql-cli version 0.6.0
Enter “.help” for usage hints.
mssql>
This means you’ve successfully connected to your instance of SQL Server.

Run a Quick Test
Run a quick test to check that SQL Server is up and running and you can query it.

For example, you can run the following command to see which version of SQL Server your running:

select @@version
If it’s running, you should see something like this (but of course, this will depend on which version you’re running):

—————————————————————————————————————————————————————————————————-
Microsoft SQL Server vNext (CTP2.0) – 14.0.500.272 (X64)
Apr 13 2017 11:44:40
Copyright (C) 2017 Microsoft Corporation. All rights reserved.
Developer Edition (64-bit) on Linux (Ubuntu 16.04.2 LTS)

1 row(s) returned

Executed in 1 ms
mssql>
If you see a message like this, congratulations — SQL Server is now up and running on your Mac!

A SQL Server GUI for your Mac – Azure Data Studio
Azure Data Studio dashboard
The Azure Data Studio dashboard.

Azure Data Studio (formerly SQL Operations Studio) is a free GUI management tool that you can use to manage SQL Server on your Mac. You can use it to create and manage databases, write queries, backup and restore databases, and more.

Azure Data Studio is available on Windows, Mac and Linux.

Here are some articles/tutorials I’ve written for Azure Data Studio:

What is Azure Data Studio
How to install Azure Data Studio on your Mac
How to Create a Database with Azure Data Studio
How to Restore a Database with Azure Data Studio on a Mac
Another Free SQL Server GUI – DBeaver
Another SQL Server GUI tool that you can use on your Mac (and Windows/Linux/Solaris) is DBeaver.

DBeaver is a free, open source database management tool that can be used on most database management systems (such as MySQL, PostgreSQL, MariaDB, SQLite, Oracle, DB2, SQL Server, Sybase, Microsoft Access, Teradata, Firebird, Derby, and more).

Screenshot of DBeaver using the Dark theme
DBeaver using the “Dark” theme.

I wrote a little introduction to DBeaver, or you can go straight to the DBeaver download page and try it out with your new SQL Server installation.

How to Install SQL Server on a Mac

Mình tạo tools để đóng các ebook, share cho các bạn muốn copy về kindle hay các thiết bị đọc sách khác để đọc nhé.
Đầu tiên mình sẽ làm dần các ebook mình thấy có hứng thú trước, các ebook mình đã đọc và thấy hay. Sau nếu còn duy trì được mình sẽ đóng các ebook khác.

Các truyện có định dạng
.mobi – đọc trên các máy kindle (hoặc điện thoại cài phần mềm kindle của amazon)
.epub – đọc trên iphone (hoặc điện thoại đọc các phần mềm có đuôi .epub)

Nguồn: Mình lấy truyện ở bachngocsach.com và isach.info

Link: Danh sách truyện

Rảnh rỗi ngồi làm cái tool cho anh em cùng đam mê kéo sách về đọc chơi (thực ra là phục vụ con kindle của mình là chính). Sẽ update dần các web nhiều truyện cho ae.

Các chức năng chính:

- Download truyện xuất ra file: docx

Sites hỗ trợ:

- Bạch ngọc sách (https://bachngocsach.com/reader/)

Link down tools: 

https://www.dropbox.com/s/2u4lgw8sy9e0b4m/BNSCrawl-1.0-SNAPSHOT.jar?dl=0

Hướng sử dụng:

01. Bạch ngọc sách
– Vào phần reader chọn sách cần download
– Link truyện có dạng: https://bachngocsach.com/reader/1q84

02. isach.info
– Vào isach.info tìm link truyện có dạng https://isach.info/story.php?story=nha_gia_kim__paulo_coelho

Bạn có thể sử dụng phương thức transaction của DB facade để chạy tập hợp các câu lệnh trong một database transaction. Nếu một exception xảy ra trong transaction Closure, transaction sẽ tự động được rolled back. Nếu Closure thực thi thành công, transaction sẽ tự động được committed. Bạn không cần lo lắng về việc phải thực hiện rolling back thủ công trong khi sử dụng phương thức transaction:

DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);

    DB::table('posts')->delete();
});

Handling Deadlocks

Phương thức transaction cho phép một tham số thứ 2 để định nghĩa số lần mà transaction sẽ được reattempted khi mà deadlock xảy ra. Khi có hiện tượng deadlock, một exception sẽ được đưa ra

DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);

    DB::table('posts')->delete();
}, 5);

Manually Using Transactions

Nếu bạn muốn quản lý transaction thủ công, bạn có thể sử dụng phương thức beginTransaction trong DB facade:

DB::beginTransaction();

Bạn có thể rollBack transaction qua phương thức rollBack:

DB::rollBack();

Cuối cùng bạn có thể commit qua phương thức commit:

DB::commit();

 Phương thức DB facade sẽ quản lý transactions cho cả 2 phương pháp là query builder và Eloquent ORM.

 

Download and Install PHP on macOS

The latest version of macOS Sierra ships with PHP 5.6 and similarly OSX 10.11 El Capitan with PHP 5.5, OSX 10.8 Mountain Lion ships with PHP version 5.3. The latest version of PHP 7.2 is available to install. The below steps to help you to install PHP 7.2 or 7.1 or 5.6 on macOS.

Open a terminal and run below commands

For PHP 7.2

curl -s http://php-osx.liip.ch/install.sh | bash -s 7.2

For PHP 7.1

curl -s http://php-osx.liip.ch/install.sh | bash -s 7.1

For PHP 5.6 – Running with OSX 10.11 El Capitan or lower versions.

curl -s http://php-osx.liip.ch/install.sh | bash -s 5.6

Verify PHP Installation

The PHP versions for macOS are maintained by php-osx and doesn’t overwrite the current php binaries installed on your system. The installs everything in /usr/local/php5. The new php binary is therefore in /usr/local/php5/bin/php.

export PATH=/usr/local/php5/bin:$PATH

To verify correct version of PHP is installed on your system, Execute the following command.

php -v

PHP 7.2.2 (cli) (built: Feb 1 2018 13:23:34) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.2, Copyright (c) 1999-2018, by Zend Technologies
with Xdebug v2.6.0, Copyright (c) 2002-2018, by Derick Rethans

Nguồn

 

Update: Để lần sau mỗi lần run không phải chạy lại lệnh EXPORT thì làm như sau

touch ~/.bash_profile; open ~/.bash_profile

Sau khi chạy lệnh thì thêm đoạn mã export ở trên vào dưới biến môi trường sẵn có là được.

Lệnh cần thêm vào file vừa mở ra (nếu ai quên)

export PATH=/usr/local/php5/bin:$PATH

 

GL&HF

Route Parameters

Required Parameters
Đôi khi bạn cần control được các thành phần của URI trong route của bạn. Ví dụ bạn có thể cần truy cập dữ liệu user's ID từ URL. Bạn có thể định nghĩa một route với tham số như sau:

Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

Bạn có thể định nghĩa nhiều tham số trong route:

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

Các tham số route luôn được đặt trong cặp ngoặc {}, và nó chỉ nên chứa các kí tự, không nên chứa kí tự -. Trong trường hợp cần sử dụng kí tự -, sử dụng dấu _ để thay thế. Các tham số này được injected vào trong callbacks/controller theo thứ tự tương ứng.
Optional Parameters
Đôi khi bạn cần tạo một tham số route, nhưng tham số này có thể là tùy chọn (có/không). Bạn có thể thực hiện điều này bằng cách đặt ? sau tên tham số. Để chắc chắn chúng ta truyền cho biến route này một giá trị mặc định:

Route::get('user/{name?}', function ($name = null) {
    return $name;
});

Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});

 

Regular Expression Constraints
Bạn có thể constrain định dạng các tham số route của bạn bằng cách sử dụng phương thức where trong một route instance. Phương thức này có 2 đối số là tên của tham số và biểu thức regular expression ràng buộc tham số này.

Route::get('user/{name}', function ($name) {
    //
})->where('name', '[A-Za-z]+');

Route::get('user/{id}', function ($id) {
    //
})->where('id', '[0-9]+');

Route::get('user/{id}/{name}', function ($id, $name) {
    //
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

 

Global Constraints
Nếu bạn muốn tham số route của bạn luôn được constrained bởi biểu thức regular expression, bạn có thể sử dụng pattern method. Bạn nên định nghĩa patterns trong phương thức boot của RouteServiceProvider:

/**
 * Define your route model bindings, pattern filters, etc.
 *
 * @return void
 */
public function boot()
{
    Route::pattern('id', '[0-9]+');

    parent::boot();
}

Khi pattern được định nghĩa, nó sẽ tự động applied vào tất cả các routes sử dụng tham số có cùng tên:

Route::get('user/{id}', function ($id) {
    // Only executed if {id} is numeric...
});

 

Named Routes

Named routes cho phép rút gọn URLs hoặc chuyển hướng tới các route đặc biệt. Bạn cũng cần chỉ ra tên cho một route bằng cách thêm phương thức name khi định nghĩa route.

Route::get('user/profile', function () {
    //
})->name('profile');

Bạn cũng có thể chỉ tên route cho các controller action:

Route::get('user/profile', 'UserController@showProfile')->name('profile');

 

Generating URLs To Named Routes
Nếu bạn muốn đặt tên cho route, bạn có thể sử dụng route's name để thực thi URLs hoặc redirect bằng cách sử dụng hàm route

// Generating URLs...
$url = route('profile');

// Generating Redirects...
return redirect()->route('profile');

Nếu route có chứa tham số, bạn có thể truyền tham số này vào đối số thứ 2 của hàm route. Tham số này sau đó sẽ được tự động truyền vào URL

Route::get('user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = route('profile', ['id' => 1]);

 

Inspecting The Current Route
Nếu bạn muốn kiểm tra xem request hiện tại được route bởi name route nào, bạn có thể sử dụng phương thức named của Route instance. Ví dụ bạn có thể kiểm tra current route name từ một route middleware:

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
    if ($request->route()->named('profile')) {
        //
    }

    return $next($request);
}

 

Route Groups

Route groups cho phép bạn chia sẻ route attributes, ví dụ như là middleware hoặc namespaces, thông qua số lượng lớn các route mà không cần định nghĩa các attributes cho từng route. Các attributes được chia sẻ  dưới dạng 1 mảng, là tham số đầu tiên trong phương thức Route::group.
Middleware
Để assign middleware cho tất cả các route trong một group, bạn có thể sử dụng phương thức middleware trước khi định nghĩa một group. Middleware thực thi lần lượt trong mảng được sắp xếp:

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // Uses first & second Middleware
    });

    Route::get('user/profile', function () {
        // Uses first & second Middleware
    });
});

 

Namespaces
Một trường hợp khác của route groups là nó được truyền giống như PHP namespace tới một nhóm các controllers bằng cách sử dụng phương thức namespace:

Route::namespace('Admin')->group(function () {
    // Controllers Within The "App\Http\Controllers\Admin" Namespace
});

Nhớ rằng, mặc định, RouteServiceProvider sẽ lấy các file route trong namespace group, cho phép bạn đăng ký các controller route mà không cần chỉ ra đầy đủ tiền tố namespace App\Http\Controllers. Vì vậy, bạn chỉ cần chỉ ra phần của namespace đằng sau App\Http\Controllers
Route Prefixes
Phương thức prefix được sử dụng để tặt tiền tố cho tất cả các route trong group. Ví dụ bạn muốn đặt prefix cho tất cả các URIs trong group admin:

Route::prefix('admin')->group(function () {
    Route::get('users', function () {
        // Matches The "/admin/users" URL
    });
});

 

Route Name Prefixes
Phương thức name được sử dụng để đặt prefix cho các route trong group. Ví dụ, bạn muốn đặt tất cả prefix của group route là admin. Một chuỗi được đặt cho route name, chúng ta cũng sẽ cung cấp một ký tự . cùng với prefix:

Route::name('admin.')->group(function () {
    Route::get('users', function () {
        // Route assigned name "admin.users"...
    })->name('users');
});

 

Route Model Binding

Khi truyền một model ID tới một route hoặc một controller action, bạn thường sẽ query để lấy về model tương ứng với ID. Laravel route model binding cung cấp một cách tiện lợi để tự động inject một thực thể model trực tiếp tới các route của bạn.
Ví dụ, thay vì truyền một user's ID, bạn có thể inject một thực thể User model.
Implicit Binding

Laravel tự động resolves Eloquent models được defined trong các route hoặc các controller action có tên match với một route segment name. Ví dụ:

Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});

Từ lúc biến $user được gợi ý như là model Eloquent trong App\User và tên biến (user) match với URI segment, Laravel sẽ tự động inject thực thể model có ID matching với giá trị từ request URI. Nếu một thực thể model matching không được tìm thấy trong database, một HTTP response 404 sẽ được trả về.
Customizing The Key Name
Nếu bạn muốn binding model tới một trường khác trong database khi lấy data từ lớp model, bạn có thể override lại phương thức getRouteKeyName trong Eloquent model:

/**
 * Get the route key for the model.
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}

 

Explicit Binding
Để đăng ký một binding tường minh, sử dụng phương thức model của router để chỉ ra lớp làm tham số. Bạn có thể định nghĩa model binding tường minh của bạn trong phương thức boot của lớp RouteServiceProvider:

public function boot()
{
    parent::boot();

    Route::model('user', App\User::class);
}

Tiếp theo định nghĩa một route chứa tham số {user}:

Route::get('profile/{user}', function (App\User $user) {
    //
});

Từ bây giờ chúng ta có thể bound tất cả tham số {user} vào model App\User, một thực thể User sẽ được injected vào route. Ví dụ, một request tới profile/1 sẽ inject thực thể User từ database có ID là 1.

Nếu không có model nào được tìm thấy trong database, 404 HTTP response sẽ được trả về.

Rate Limiting

Laravel có một middleware để giới hạn truy cập tới các route trong ứng dụng của bạn. Để bắt đầu, sử dụng throttle middleware để điều khiển một route hoặc một nhóm các route. Tthrottle middleware chấp nhận 2 tham số, maximum number của các request được thực thi trong một phút. Ví dụ, Chỉ ra việc authenticated user có thể được truy cập 60 lần/phút

Route::middleware('auth:api', 'throttle:60,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});

 

Dynamic Rate Limiting
Bạn cũng có thể chỉ ra số request lớn nhất động, dựa trên một thuộc tính của authenticated User model. Ví dụ, nếu User model của bạn chứa một biến rate_limit, bạn có thể truyền tên của thuộc tính này tới throttle middleware, vì thế nó được sử dụng để tính toán số request tối đa:

Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});

 

Form Method Spoofing

HTML forms không hỗ trợ các action PUT, PATCH, DELETE. Vì thế, khi defining các route PUT, PATCH hoặc DELETE được gọi từ HTML form, bạn sẽ cần thêm một hidden _method field cho form của mình. Giá trị này sẽ được sử dụng trong giao thức HTTP request:

<form action="/foo/bar" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>

Bạn cũng có thể sử dụng method @Blade để tạo _method input:

<form action="/foo/bar" method="POST">
    @method('PUT')
    @csrf
</form>

 

Accessing The Current Route

Bạn có thể sử dụng phương thức current, currentRouteNamecurrentRouteAction của route facade để truy cập thông tin về route đang được handling trong request:

$route = Route::current();

$name = Route::currentRouteName();

$action = Route::currentRouteAction();

Tham khảo thêm thông tin về Route FacadeRoute Instance

Thông qua bài viết này mình sẽ hướng dẫn các bạn làm quen và hiểu thêm về công nghệ Blockchain.

Mục đích

– Tạo hệ thống Blockchain đơn giản nhất.
– Tạo hệ thống mining (poor of work) đơn giản.
– Khám phá thêm về Blockchain.

Kiến thức cần chuẩn bị: OOP, biết 1 ngôn ngữ hướng đội tượng.
Ok giờ xắn tay vào việc nào

Making the Blockchain.

Một blockchain là danh sách, chuỗi các khối (A blockchain is just a chain/list of blocks). Mỗi block trong blockchain sẽ chứa chữ ký số của nó, chữ ký số của khối trước nó và dữ liệu của khối (dữ liệu giao dịch là một ví dụ).

 

Hash = Digital Signature.

Mỗi khối không chỉ chứa mã hash của khối trước nó mà nó còn chứa mã hash của chính nó, được tính toán dựa trên mã hash của khối trước nó. Nếu dữ liệu khối trước đó bị thay đổi (khối A), việc này dẫn tới thay đổi mã hash của khối A, do mã hash được tính toán dựa trên data này. Việc này dẫn tới sự thay đổi mã hash của tất cả các khối trong chuỗi. Việc tính toán và so sánh giúp chúng ta phát hiện ra bất kì sự thay đổi nào trong blockchain.

Điều này có nghĩa là gì? – Nghĩa là khi thay đổi dữ liệu của 1 block thì sẽ dẫn tới thay đổi dữ liệu của rất rất nhiều block, và dãn tới thay đổi chain (break the chain).

Giờ chúng ta sẽ thừ bắt đầu xây dựng một hệ thống Blockchain đơn giản.

Tạo project đặt tên là NoobChain

Thêm class Block để xây dựng blockchain

class Block
{
        public String hash;
        public String previousHash;
        private String data; // Trong ví dụ này chúng ta chỉ lưu data là một thông báo.
        private long timeStamp;
        

        //Block Constructor.
        public Block(String data, String previousHash)
        {
            this.data = data;
            this.previousHash = previousHash;
            this.timeStamp = DatetimeHandle.GetTime();            
        }     

}

Như bạn thấy Block cơ bản của chúng ta chứa một chuỗi hash sẽ dùng để lưu chữ kí số. Một biến previousHash để lưu hash của khối trước nó, và dữ liệu của khối.

Bước tiếp theo chúng ta sẽ tiến hành tạo chữ ký số.
Tạo lớp HashSha256, áp dụng thuật toán sha256 để sinh chuỗi, với nội dung như sau:

class HashSha256
{
  public override string Hash(string strInput)
  {
    try
    {
      var crypt = new System.Security.Cryptography.SHA256Managed();
      var hash = new System.Text.StringBuilder();
      byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(strInput));
      foreach (byte theByte in crypto)
      {
        hash.Append(theByte.ToString("x2"));
      }
      return hash.ToString();
    }
    catch (Exception e)
    {
      throw e;
    }    
  }
}

Sau đó quay trờ lại lớp Block thêm và hàm tính mã hash

public String CalculateHash()
{
  HashSha256 sha256 = new HashSha256();
  String calculatedhash = sha256.Hash(
      previousHash +
      timeStamp.ToString()
      data);
  return calculatedhash;
}

 

.. và thêm hàm này vào contructor của lớp Block

public Block(String data, String previousHash)
{
  this.data = data;
  this.previousHash = previousHash;
  this.timeStamp = DatetimeHandle.GetTime();
  this.hash = CalculateHash(); // thêm hàm này vào cuối để chắc chắn các dữ liệu khác được init trước khi gọi hàm
}

Ok, giờ chúng ta thử kiểm tra lại công việc vừa làm xem…
Trong hàm Main của class program.cs chúng ta sẽ viết đoạn code để hiển thị chuỗi hash

Khối đầu tiên được gọi là genesis block, và vì nó không có khối phía trước nên ta sẽ gán previousHash của nó bằng 0

class Program
{       
  static void Main(string[] args)
  {
    Block genesisBlock = new Block("Hi im the first block", "0");
    Console.WriteLine("Hash for block 1 : " + genesisBlock.hash);

    Block secondBlock = new Block("Yo im the second block", genesisBlock.hash);
    Console.WriteLine("Hash for block 2 : " + secondBlock.hash);

    Block thirdBlock = new Block("Hey im the third block", secondBlock.hash);
    Console.WriteLine("Hash for block 3 : " + thirdBlock.hash);          

    Console.ReadLine();
  }
}

Output của chúng ta sẽ là 3 mã hash


Mỗi block giờ sẽ chứa chữ kí số được xây dựng trên thông tin của nó và chữ ký số của khối phía trước.
Để giống với khái niệm blockchain hơn, ta sẽ lưu trữ các block này trong List
Chỉnh sửa file program.cs như sau:

class Program
{
  public static List<Block> blockchain = new List<Block>();

  static void Main(string[] args)
  {
    blockchain.add(new Block("Hi im the first block", "0"));		
    blockchain.add(new Block("Yo im the second block",blockchain.ElementAt(blockchain.size()-1).hash)); 
    blockchain.add(new Block("Hey im the third block",blockchain.ElementAt(blockchain.size()-1).hash));

    string printBlockChain = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(blockchain);
    Console.WriteLine(printBlockChain);

    Console.ReadLine();
  }
}

Để sử dụng được JavaScriptSerializer thì các bạn làm như sau

Right click References and do Add Reference, then from Assemblies->Framework select System.Web.Extensions

Chạy lại ứng dụng và xem kết quả thử nhé mọi người 🙂

 

Kiểm tra tính toàn vẹn của blockchain.

Chúng ta sẽ viết phương thức IsChainValid() kiểu boolean trong file program.cs. Phương thức nãy sẽ loop qua tất cả các block trong chain và so sánh các chuỗi hash. Phương thức này sẽ cần kiểm tra biến hash so sánh nó với chuỗi hash được tính toán, và chỗi hash ở khối trước so sánh với biến previousHash. Uhm, nghe có vẻ phức tạp, thử nhìn code xem có dễ hiểu hơn không nào.

public static Boolean IsChainValid()
{
  Block currentBlock;
  Block previousBlock;

  //loop through blockchain to check hashes:
  for (int i = 1; i < blockchain.Count; i++)
  {
    currentBlock = blockchain.ElementAt(i);
    previousBlock = blockchain.ElementAt(i - 1);

    //compare registered hash and calculated hash:
    if (!currentBlock.hash.Equals(currentBlock.CalculateHash()))
    {
      Console.WriteLine("Current Hashes not equal");
      return false;
    }

    //compare previous hash and registered previous hash
    if (!previousBlock.hash.Equals(currentBlock.previousHash))
    {
      Console.WriteLine("Previous Hashes not equal");
      return false;
    }
  }
  return true;
}

Bất kì một thay đổi nào với mỗi block phương thức này sẽ trả về false.

Trên mạng lưới bitcoin, các nodes chia sẻ các blockchain và chuỗi hợp lệ dài nhất sẽ được châp nhận bởi hệ thống. Vậy nếu một hacker giả mạo một khối và tạo nên chuỗi blockchain mới cho mạng thì sao. Điều này là bất khả thi, vì sao? Giả sử hacker có thể xâm nhập và thay đổi một khối dữ liệu, điều này dẫn tới các dữ liệu tiếp theo của khối này cũng bị thay đổi. Vì vậy cần rất nhiều thời gian và sức mạnh tính toán để hacker có thể vượt hơn được so với sức mạnh tính toán của các thành viên còn lại trong hệ thống khi kết hợp.

Đào tiền ảo, cày sâu hay lướt sóng?

Lets start mining blocks!!!

Chúng ta sẽ yêu cầu minner tiến hành đào-coin bằng cách bắt họ phải thử các biến khác nhau trong block cho đến khi chuỗi hash bắt đầu với giá trị 0. Tại sao lại là 0?. Bạn có thể quy ước là bất cứ giá trị gì, tùy thuộc vào genesis hash của bạn. Trong trường hợp này chúng ta chọn 0.

Tiếp theo chúng ta thêm biến nonce trong hàm CalculateHash(), và thêm hàm MineBlock() như sau:

class Block
{
  public String hash;
  public String previousHash;
  private String data; //our data will be a simple message.
  private long timeStamp; //as number of milliseconds since 1/1/1970.
  private int nonce = 0;

  //Block Constructor.
  public Block(String data, String previousHash)
  {
    this.data = data;
    this.previousHash = previousHash;
    this.timeStamp = DatetimeHandle.GetTime();
    this.hash = CalculateHash();
  }

  public String CalculateHash()
  {
    HashSha256 sha256 = new HashSha256();
    String calculatedhash = sha256.Hash(
        previousHash +
        timeStamp.ToString() +
        nonce.ToString() + 
        data);
    return calculatedhash;
  }

  public void MineBlock(int difficulty)
  {
    var str = new String(new char[difficulty]);
    String target = new String(new char[difficulty]).Replace('\0', '0'); //Create a string with difficulty * "0" 
    while (!hash.Substring(0, difficulty).Equals(target))
    {
      nonce++;
      hash = CalculateHash();
    }
    Console.WriteLine("Block Mined!!! : " + hash);
  }
}

Phương thức MineBlock() truyền vào tham số difficulty kiểu int để quy định độ khó của thuật toán. Các bạn có thể thay đổi tham số này để kiểm tra tốc độ đào-coin khi chạy chương trình. Ở ví dụ này mình khuyến khích các bạn đặt ở mức 4-6 cho việc testing. Ex, Hiện tại độ khó của Litecoin là khoảng 442,592, khá kinh khủng.

Chúng ta thêm biến difficulty vào lớp program.cs:

public static int difficulty = 5;

Tiếp đó chúng ta cập nhật lớp program.cs, gọi hàm MineBlock() cho mỗi block mới. Phương thức IsChainValid() cũng được gọi sau khi đào được coin :D.

class Program
{
  public static List<Block> blockchain = new List<Block>();
  public static int difficulty = 5;

  static void Main(string[] args)
  {
    blockchain.Add(new Block("Hi im the first block", "0"));
    Console.WriteLine("Trying to Mine block 1... ");
    blockchain.ElementAt(0).MineBlock(difficulty);

    blockchain.Add(new Block("Yo im the second block", blockchain.ElementAt(blockchain.Count - 1).hash));
    Console.WriteLine("Trying to Mine block 2... ");
    blockchain.ElementAt(blockchain.Count - 1).MineBlock(difficulty);

    blockchain.Add(new Block("Hey im the third block", blockchain.ElementAt(blockchain.Count - 1).hash));
    Console.WriteLine("Trying to Mine block 3... ");
    blockchain.ElementAt(blockchain.Count - 1).MineBlock(difficulty);

    Console.WriteLine("\nBlockchain is Valid: " + IsChainValid());
    string printBlockChain = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(blockchain);
    Console.WriteLine(printBlockChain);

    Console.ReadLine();
  }

  public static Boolean IsChainValid()
  {
    Block currentBlock;
    Block previousBlock;

    //loop through blockchain to check hashes:
    for (int i = 1; i < blockchain.Count; i++)
    {
      currentBlock = blockchain.ElementAt(i);
      previousBlock = blockchain.ElementAt(i - 1);

      //compare registered hash and calculated hash:
      if (!currentBlock.hash.Equals(currentBlock.CalculateHash()))
      {
        Console.WriteLine("Current Hashes not equal");
        return false;
      }

      //compare previous hash and registered previous hash
      if (!previousBlock.hash.Equals(currentBlock.previousHash))
      {
        Console.WriteLine("Previous Hashes not equal");
        return false;
      }
    }
    return true;
  }
}

Chạy thử và xem kết quả nhé

Ối zồi ôi tôi đào được coin rồi này 😀

Kết

Một blockchain giả mạo sẽ không thể bắt kịp chuỗi dài hơn và hợp lệ trừ khi chúng có tốc độ tính toán lớn hơn tất cả các nút khác trong mạng của bạn kết hợp. Một máy tính lượng tử trong tương lai hay gì đó (người ngoài hành tinh?)

Demo

https://github.com/kumochan/Blockchain-Sample-01

Basic Routing

Các routes cơ bản của Laravel chấp nhận một URI và một Closure, nó cung cấp một phương thức rất đơn giản và dễ hiểu để định nghĩa routes như sau:

Route::get('foo', function () {
  return 'Hello World';
});

 
The Default Route Files
Tất cả các routes của Laravel được định nghĩa trong file route được đặt trong thư mục routes. Những files này được tự động load bởi framework. routes/web.php định nghĩa các routes cho ứng dụng web của bạn. Các routes này được gán cho nhóm web middleware, nhóm này sẽ cung cấp các tính năng như sesion stateCSRF protection. Các routes trong file routes/api.phpstateless - không có trạng thái và được gán cho nhóm api middleware

Hầu hết các ứng dụng, bạn sẽ bắt đầu bằng việc định nghĩa routes trong file routes/web.php. Routes được định nghĩa trong file routes/web.php có thể được truy cập bằng URL trên trình duyệt. Ví dụ, bạn có thể truy cập route tới http://your-app.test/user trên trình duyệt như sau:

Route::get('/user', 'UserController@index');

Routes được định nghĩa trong file routes/api.php được lồng trong một route group bởi RouteServiceProvider. Trong group này, /api URI prefix tự động được thêm vào, vì thế bạn không cần thêm nó một các thủ công cho mỗi route trong file. Bạn có thể chỉnh sửa prefix và các tùy chỉnh của route group bằng cách chỉnh sửa lớp RouteServiceProvider.
 
Available Router Methods
Router cho phép bạn đăng ký các routes trả về từ bất kì giao thức HTTP nào:

Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

Đôi khi bạn cần đăng ký một route chấp nhận nhiều giao thức HTTP. Trường hợp này bạn có thể sử dụng phương thức match. Hoặc bạn thậm chí có thể đăng ký một route cho phép nhận response từ tất cả các giao thức HTTP bằng cách sử dụng phương thức any:

Route::match(['get', 'post'], '/', function () {
    //
});

Route::any('foo', function () {
    //
});

 
CSRF Protection
Bất kì giao thức POST, PUT, DELETE nào được gửi qua web route sẽ phải kèm theo một csrf token. Nếu không request này sẽ bị từ chối.

<form method="POST" action="/profile">
    @csrf
    ...
</form>

Redirect Routes
Nếu bạn muốn chuyển hướng tới một URI khác, bạn có thể sử dụng Route::redirect. Sử dụng phương thức này bạn không cần phải định nghĩa một route đầy đủ hoặc một controller để thực thi redirect đơn giản:

Route::redirect('/here', '/there', 301);

 
View Routes
Nếu route của bạn chỉ trả về một view, bạn có thể sử dụng phương thức Route::view. Tương tự như phương thức redirect, phương thức này trả về một view mà bạn không cần phải định nghĩa một route ddầy đủ hoặc một controller. Tham số đầu tiên của phương thức này là một URI, tham số thứ 2 là tên view. Ngoài ra bạn có thể truyền một mảng dữ liệu để hiển thị trên view:

Route::view('/welcome', 'welcome');

Route::view('/welcome', 'welcome', ['name' => 'Taylor']);

 
Phần 2