2020年3月26日 星期四

dart 程式庫(package)管理

參考官網文章:  https://dart.dev/guides/packages
dart 使用檔案 pubspec.yaml 指引程式庫(package)的運作邏輯, pubspec.yaml 裡面最重要的是 name: 指引,用來標示專案的名稱,同時也代表該專案程式庫(package)所處最上層路徑, 將專案名字寫在檔案裡面的好處是:若將最上層目錄搬移或更名也不影響程式的運作. 其次是 dependencies: 區塊指引,用來紀錄該專案與外部程式庫之相依性, 相關的程式庫須用 tab 鍵或是空白鍵先縮排(indent),再使用"名稱:版本"的格式一一分行列表, 另外可以用符號 # 當註解,例如:
#pubspec.yaml
name:  myPackage
dependencies:
  ffi: 0.1.3  #外部程式庫 ffi, 需求版本 0.1.3
  crypto: 2.1.4  #外部程式庫 crypto, 需求版本 2.1.4

開啟終端機, 執行 pub get 後就會根據 pubspec.yaml 指引去安裝(或下載)好相關的程式庫,安裝好的結果會儲存在檔案 .packages 裡面. 整個專案目錄結構示範如下:
myPackageRoot -  #專案根目錄
      lib -  #內定專屬程式庫(package)目錄起點
            maintain.dart  #程式庫維護原始碼
            src -  #專案其它程式原始碼目錄
                  utility.dart  
                  help.dart
     main.dart #專案主程式原始碼

內定專屬目錄 lib 會被當成是專案外部程式庫的參考起點,通常在 dart 代碼若是一開頭使用 import 'package: ...' 或是 export '...' 時, 就會以 lib 目錄當參考點去引入相關的檔案,因此可以準備一個檔案例如放在 lib/maintain.dart 專門當作程式庫維護程式碼, 在裡面用 export 方式間接引入檔案或是用 import 方式直接引入各項程式庫. 通常習慣是在 lib 目錄下再新增一個目錄名稱,例如 src, 把該專案的其餘程式全放在 lib/src 目錄底下(如上述範例)當成該專案的專屬程式庫, 未來只要將該目錄(src)複製到其他專案裡面, 就能重複使用此專屬程式庫,甚至萬一把程式庫目錄 src 改名了, 也只要修改維護程式內的相對目錄 src 就可. 總之, 目錄結構更動是維護程式碼的主要任務,盡可能不影響程式庫的運作邏輯:
// maintain.dart
export 'src/utility.dart';  // 間接從路徑 lib/src 引入檔案 utility.dart
export 'src/help.dart'; // 間接從路徑 lib/src 引入檔案 help.dart
// import 'dart:html';// 直接引入 dart 核心程式庫 html
// ...

經過上述規劃後, 主程式只要將 maintain.dart 引入就可, 乾淨俐落, 但須注意的是, 當要引入該專案的專屬程式庫(package)時, 須先指明專案名稱, 換句話說專案名稱也同時身兼專案專屬程式庫的角色:
// main.dart
import 'package:myPackage/maintain.dart'; //直接從 myPackage 引入 lib/maintain.dart
main( ) {
      // ...
}

2020年3月25日 星期三

從 dart 呼叫 javascript


參考官網文章:  http://dartdoc.takyam.com/articles/js-dart-interop/


// index.html

<html><head><meta charset='utf-8'></head><body>
    <script type='application/javascript' src='hello.js'></script>
    <script type='application/javascript' src='main.js'></script>
</body></html>

// hello.js
var greeting="Hello"; // 1. variable

function fun( ){ //2. function
    console.log("How are you?");
}

class Rectangle {
    constructor(width, height) {
      this.width = width;
      this.height = height;
    }
}
function rectangle(width, height) { // 3. object
  return new Rectangle(width, height); // return an object
}

// main.dart
import 'dart:js';
void main() { 

  var greeting = context['greeting']; // 1.
  print("$greeting");

  context.callMethod('fun'); //2.

  final rectangle = JsObject(context['rectangle'], [5, 1]); //3.
  print(rectangle['width']);
}

2020年3月23日 星期一

dart 設定好開發 javascript 的工作環境: dart2js

1. 開啟 vscode, 搜尋並安裝好插件 debugger for Chrome

2. 開啟專案的目錄

3. 設定好執行 debug 的環境, 準備讓 chrome 開啟 index.html 檔案, 只要點選 vscode 的選單 Debug -> Open Configurations, 在開啟 launch.json 後, 填入以下文字, 主要是填入讓 chrome 開啟 url 檔案所在位置, 並設定好工作的根目錄:
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "執行 Chrome 開啟 index.html 檔",
            "url": "file:///${workspaceFolder}/index.html",
            "webRoot": "${workspaceFolder}"
        }
    ]
}

4. 設定好將 dart2js 的環境, 準備將 dart 語法轉換為 javascript, 只要點選  vscode 的選單 Terminal -> Configure Tasks, 在開啟 tasks.json 後,  填入以下文字, 主要是將 dart2js 執行檔的絕對路徑填入 "command" 所指示的位置, 並設定好一些必要參數 args:
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "dart2js",
            "type": "shell",
             "command": "/home/mint/Downloads/flutter/bin/cache/dart-sdk/bin/dart2js",
            "args": [
                "${file}",

                "-o",
                "${fileBasenameNoExtension}.js"
            ],

            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

5. 撰寫 index.html 要讓 Chrome開啟 url 後, 自動執行經 dart2js 轉換後的 main.js:
 <html><head><meta charset='utf-8'></head><body>
    <script type='application/javascript' src='main.js'></script>
</body></html>

6. 用 dart 語言在 html 的 canvas 上繪製 sin(2πft) 函數圖, 範例主程式 main.dart:
// main.dart
class CanvasSin {
  final DivElement  div  = document.createElement('div');
  final CanvasElement canvas = document.createElement('canvas');
  final int width, height;
  get fullYscale => height~/2;
  CanvasRenderingContext2D get ctx => canvas.getContext("2d");
  void penline(int x1, int y1, int x2, int y2) => ctx .. strokeStyle="red" ..
       beginPath( ) .. moveTo(x1, y1) .. lineTo(x2, y2) .. stroke( );
  void get penOrigin => ctx .. save( ) .. translate(0, fullYscale) .. scale(1, -1) ..
       beginPath( ) .. moveTo(0, 0);
  void get penFinish => ctx .. strokeStyle="green" .. stroke( ) .. restore( );
  void penTo(int x, double y) => ctx.lineTo(x, y);
  CanvasSin(this.width, this.height) { 
      document.body.append(div);
      div.append(canvas);
      canvas.style.border = "1px solid";//外框 1 點
      canvas.width  = width;
      canvas.height = height;
      penline(0, 0, 0, height);// y-axis
      penline(0, fullYscale, width, fullYscale);// x-axis
      int freq = 2;
      double f(int t) => fullYscale * sin(2.0*3.14159*freq*t/width);// sin(2πft)
      penOrigin;
      for(int t = 0; t < width; t ++) penTo(t, f(t));   
      penFinish;
  }
}
void main( ) => CanvasSin(800, 600);

7.目前 dart2js 僅能編譯整個內含 main( ) 主程序的 dart 原始碼, 無法針對個別單一程式碼去編譯, 經上述步驟 4 設定好後, 要將目前用 dart 語言編輯的主程式轉換為相對應的 javascript 程式碼, 只要按下快捷鍵 ctrl-shift-B, 或是用選單 Terminal -> Run Task 選取 dart2js 直接編譯

8. 經上述步驟 1~6 後, 按下快捷鍵 F5, 或是用選單 Debug -> Start Debugging  就能讓 Chrome 直接開啟 url 執行所轉換後的 javascript


2020年3月20日 星期五

用 dart 語言簡單驗證 ECDSA 演算法

dart 語言支援大數(BigInt)的四則運算,且又能將運算元重新定義運算規則(operator overload),而且 dart 在 2.7 版以後支援了 extension 語法, 可以自行添加方法到 BigInt 上, 簡直如虎添翼,因此驗證 ECC(橢圓曲線密碼學) 的數位簽證演算法(ECDSA)就容易多了:
// main.dart to verify ECDSA
import 'dart:math';
BigInt random(BigInt  m) {
        BigInt temp = BigInt.from(0);
        int bits = m.bitLength;
        while (bits > 0) {
            temp = temp << 8 |  BigInt.from(Random(DateTime.now( ).microsecondsSinceEpoch).nextInt(255));
            bits -= 8;
        }
        return temp % m;
}
extension Divider4ECDSA on BigInt {  // a method .__ to operate / % m
  BigInt __(BigInt divisor, BigInt m) => this * divisor.modInverse(m) % m;
}
class ModField {
  BigInt _value;
  BigInt get value => _value;
  int get mostbits => _value.bitLength - 1;
  static BigInt _prime;
  BigInt get p {
    if(_prime == null) _prime = BigInt.parse("115792089237316195423570985008687907853269984665640564039457584007908834671663");
    return _prime;
  }
  static BigInt parse(var temp) =>
      temp is ModField ? temp._value   :
      temp is String   ? BigInt.parse(temp):
      temp is num     ? BigInt.from(temp) :
      temp is BigInt  ? temp : BigInt.zero;
  bool testbit(int k) => _value >> k & BigInt.one == BigInt.one;
  bool operator ==(var other) => _value == parse(other)
  bool operator < (var other) => _value < parse(other);
  bool operator > (var other) => _value > parse(other);
  ModField operator ^ (int exponent) => ModField(_value.pow(exponent));
  ModField operator + (var other) => ModField(_value + parse(other));
  ModField operator - (var other) => ModField(_value - parse(other));
  ModField operator * (var other) => ModField(_value * parse(other));
  ModField operator %(var other) => ModField(_value % parse(other));
  ModField operator / (var other) => ModField(_value*parse(other).modInverse(p));
  ModField (var temp) {
        _value = parse(temp) % p;
        if (_value < BigInt.zero) _value =  _value + p;
  }
}
class Secp256k1 { // y^2 = x^3 + 7 mod p, ECC order number= GFn
  ModField n, x, y;
  bool get isPoint => y^2 == (x^3) + 7;
  Secp256k1(var temp) {
        if (temp is Secp256k1) { // copy constructor
            n = temp.n;
            x = temp.x;
            y = temp.y;
        } else {
            n = ModField(temp);
            x = ModField("55066263022277343669578718895168534326250603453777594175500187360389116729240");
            y = ModField("32670510020758816978083085130507043184471273380659243275938904335757337482424");
            if(n > 1) {
                 _selfMul(n);// 1 Gn(x, y)
                 n = ModField(1);
            }
        }
  }
  Secp256k1._point(gx, gy) {
        n = ModField(1);
        x = ModField(gx);
        y = ModField(gy);
  }
  factory Secp256k1.G([var gx=0, var gy = 0]) {
       if(gy == 0)  return  Secp256k1(1);
       final temp = Secp256k1._point(gx, gy);
       return temp.isPoint ?  temp :  Secp256k1(1);
  }
  Secp256k1 get _selfX2 { // point doubler
       final px = x;
       final s = (x^2) * 3 / (y*2); // s = 3*x*x / 2y
       x = (s^2) - x*2; // x = s*s - 2x
       y = s * (px - x) - y; // y = s * (px - x) - py
       return this;
  }
  Secp256k1 _selfAdd(ModField gx, ModField gy, {gn = 1}) { // point adder
      if (x == gx)  n = ModField(0);
      else if (n == 0) {
                x = gx;
                y = gy;
                n = ModField(gn);
      } else {
              final s = (y - gy) / (x - gx);
              x = (s^2)  - x - gx; // s*s - x - gx
              y = s*(gx - x) - gy; // s(gx -x) - gy
      }   
      return this;
  }
  Secp256k1 _selfMul(ModField multiplier) {// point scale by DAA
      if (multiplier > 1) {
            var bits =  multiplier.mostbits;
            final gx = x;
            final gy = y;
            while (bits-- > 0) {
                _selfX2;
                if (multiplier.testbit(bits))  _selfAdd(gx, gy);
            }
      }
      return this;
  }
  get dump => print("${n.value} G(${x.value},${y.value})");
  static BigInt _order;
  get GFn {
    if(_order == null) _order = BigInt.parse("115792089237316195423570985008687907852837564279074904382605163141518161494337"); 
    return _order;
  }
  operator + (Secp256k1 Q) => Secp256k1.G(x, y)._selfAdd(Q.x, Q.y);
  operator * (var scale)   => Secp256k1.G(x, y)._selfMul(ModField(scale));
  List<BigInt> signECDSA(BigInt d, BigInt z) { // use key d to sign z, % use GFn 
        final BigInt k = random(GFn);
        final BigInt r = Secp256k1(k).x.value; // r = G.x
        final BigInt s = (z + r * d) .__ (k, GFn); // s = (z + r * d)/k % GFn
        return [z, r, s];
  }
  bool checkECDSA(List<BigInt> signature) {
        if(signature.length < 3) return false;
        final BigInt z = signature[0];
        final BigInt r = signature[1];
        final BigInt s = signature[2];
        final BigInt u  = z .__ (s, GFn); // u = z/s % GFn;
        final BigInt v  = r .__ (s, GFn);  // v = z/s % GFn;
        final Secp256k1 R = Secp256k1.G( )*u + Secp256k1.G(x, y)*v;
        return R.x == r;
  }
}
main( ){
  final text = BigInt.from(123);// 待簽字
  final secret =  BigInt.from(123456789);  // 私鑰
  final qPublic = Secp256k1(secret); // 公鑰
  final signature  = qPublic.signECDSA(secret, text);// 用 ECDSA 簽證文字產生特徵碼
  qPublic.dump;
  print( qPublic.isPoint);
  print(qPublic.checkECDSA(signature) ? "OK": "Fake!");// 用公鑰驗證特徵碼
}
執行 dart main.dart 看結果:
1 G(4051293998585674784991639592782214972820158391371785981004352359465450369227 ,
    88166831356626186178414913298033275054086243781277878360288998796587140930350)
true
OK

2020年3月16日 星期一

用 dart 語言產生一個簡單的區塊鏈

先寫一個 pubspec.yaml 檔案用來下載 package:crypto, 接著開啟終端機執行 pub get 就可:
// pubspec.yaml
name: blockchain
version: 0.0.1
description: >-
  A super simple example of blockchain
author: masontseng
environment:
  sdk: '>=2.6.0 <3.0.0'
dependencies:
  crypto: ^2.1.4

編輯主程式 main.dart, 接著開啟終端機執行 dart main.dart 看執行結果:
// main.dart
import 'dart:convert';
import 'package:crypto/crypto.dart';
class Block {
    final int nonce, index, timestamp;
    final String data, pre_hash;
    get plain  => "${nonce}-${index}-${data}-${timestamp}-[${pre_hash}]";
    get hash  => sha256.convert(utf8.encode(plain)).toString( );
    get dump => print("${plain} [${hash}]");
    factory Block.genesis( ) => Block(0, 0, "genesisBlock", 1584332296607000, "0");
    Block(this.nonce, this.index, this.data, this.timestamp, this.pre_hash);
}
class BlockChain {
    var field = [ ]; //field of blockchain is a matrix
    set blockchain(var temp) {
      if(temp.length <= field.length) return;
      if(temp[0].data != field[0].data) return;
      for (var i = 1; i < field.length; i++) if (! isBlockValid(temp[i], field[i - 1])) return;
      for (var i = field.length; i < temp.length; i++) if (! isBlockValid(temp[i], temp[i - 1])) return;
      field = temp;
    }  
    get blockchain => field;
    get dump { for (var i = 0; i < field.length; i++) field[i].dump; }   
    Block powMineBlock(String eData, {int nonce=0}){
      final lastblock = field[field.length - 1];
      final timeNow = DateTime.now( ).microsecondsSinceEpoch;
      return Block(nonce, lastblock.index + 1, eData, timeNow, lastblock.hash);
    }
    void addBlock(String e) => field.add(powMineBlock(e, nonce: 1));
    bool isBlockValid(Block block, Block pre) =>
      block.index    == pre.index + 1  &&
      block.pre_hash == pre.hash
    bool isValid( ) {
      for (var i = 1; i < field.length; i++) if (! isBlockValid(field[i], field[i - 1])) return false;
      return true;
    }
    BlockChain( ) {  field.add(Block.genesis( )); }
}
main( ){
  final c = BlockChain( );
  for(int i=1; i<10; i++)  c.addBlock("extendBlock${i}");
  c.dump;
  print(c.isValid( ));

  final d = BlockChain( );
  d.blockchain = c.blockchain;
  d.dump;
  print(d.isValid( ));
}
後記:
1. 理解 proof of work (pow): 是一個群體共識決(交易方法),他是針對一個數學問題,很容易驗證但很難找到答案,或許只能用嘗試錯誤法,有點類似樂透的方式猜出答案,但不管如何,只要能解答讓大部分的人同意並形成的共識決,最後將結果紀錄於區塊鏈內,解題者就可獲取報酬,缺點是要浪費電腦的計算力
2. 理解 proof of stake (pos): 也是一個群體共識決(交易方法),交易發行者針對交易行為事先在網路上廣播,擬參與交易認證者事先以一定比例(賭注/持有者資金)資金注入系統,該筆資金將在網路中暫時封鎖無法交易,有點類似價金履約保證的概念當作履約保證金,此封鎖的資金稱為 stake,擬入區塊鏈的區塊就由該履約保證者提出並上呈網路供公眾確認.最後一旦交易完成並進入區塊鏈,提出履約保證者就能獲得一定比例的報酬作為手續費,封鎖的資金也同時解凍,但若違反規則,也會獲得懲罰,像是沒收保證金等等. pos 解決了耗費電腦運算的麻煩,但隨之而來的是要解決 nas(Nothing At Stake)的問題.

2020年3月14日 星期六

關於武漢肺炎

       武漢肺炎(covid-19) 源自中國大陸武漢地區, 起初(約2019年12月)一些中國年輕醫生(李文亮等8名醫生)發覺有一批不明原因肺炎導致死亡的案例, 在社群軟體內提醒親朋好友注意小心防患這種類 SARS 病毒再次掘起,也因此導致被中國的公安約談,訓斥其擾亂社會秩序的行為,並且禁止其再發表武漢肺炎相關的言論,中國官方甚至還大言不慚宣稱不會人傳人,到了2020年1月23日中國官方卻展開對武漢封城的行動, 許多人徹夜逃離武漢,病毒也因此開始向四處擴散,同日台灣在機場檢疫篩檢時發現了第一個武漢肺炎案例,是一個從武漢回來的台商,至於中國大陸的李醫生也隨後在醫院被傳染,並且不幸於 2020年2月6日死於武漢肺炎,在中國內部網路上一度掀起中國言論自由的浪潮追悼李醫生,呼籲響應"#不能#不明白"的網路行動,但是在中國強制言論封鎖措施下,這些言論在中國內部一夕之間化為烏有,甚至各種相關的關鍵詞也消失殆盡.各種報章媒體紛紛追起武漢肺炎的源頭,最終指向大約在去年(2019)11月中一間武漢醫院落腳的零號病人,於此展開傳染與肆虐,因此才會命名為武漢肺炎.世界衛生組織(WHO)卻受惠於中國大陸資金的援助,卻不敢對中國的隱匿疫情,延遲傳染病處置措施,沒保護人民免於疾病之威脅等等提出任何挑戰,這些理當是一個自稱世界衛生機構組織當有的基本作為與主張,不僅如此,秘書長譚得賽等一些WHO的領袖們反而對中國百般的歌功頌德,讚美中國的封城舉動,殊不知其已陷入中國共產黨大外宣的陷阱當中,最終將導致世界大爆發,一場人類最嚴重的瘟疫即將展開,WHO無疑是一個最大幫凶,甚至還曾以不要造成污名化(中國武漢)為由,更改病毒名稱成現在的 covid-19. 現今世界上有多少是因發生地點而命名的致命病毒,像是德國麻疹,日本腦炎,西班牙流感,伊波拉病毒等等,都是要讓世人了解謹記病毒歷史的教訓避免事件重演,它只是一個世界文化的遺產,完全不會有污名化的餘慮或觀感.尊敬歷史並理解歷史根源才是對一個歷史事件發生所需的正確態度,一昧的將它從歷史中抹除,無疑就是一個歷史罪人,正所謂"此地無銀三百兩",像WHO這種一反常態的行為與舉止可想而知,其信用將跌入谷底,無怪乎有人戲稱WHO為CHO(附隨中國的衛生組織).
        近日中國官方甚至丟出陰謀論說武漢肺炎來自美國,中國共產黨真是無恥到極點.要知道唯有中國共產黨才會一心一意想要洗清歷史罪名,就好比當年文化大革命般,實際卻是對歷史鬥爭將黑的歷史說成白的,扭轉人民的思想,再譬如美其名的新疆再教育營,實際卻是一個思想改造大集中營,另外還有中國共產黨在1989年6月4日天安門大規模用機關槍及坦克屠殺學生的事件歷歷在目,但對中國內部卻直接將該段歷史抹除,如此般的玩弄文字遊戲,美化及合理化其所作所為,企圖就是要讓人民忘記中國共產黨所造就的人類浩劫,竄改歷史就是不想反省之前犯下的罪過,只要將壞事從歷史上消除,讓人以為世界太平,這才是它一貫的技倆與目的,居住在中國大陸的年輕人,現恐怕早被洗腦對該#8964事件完全沒有任何記憶.這就是中國共產黨最厲害的地方,它讓你對他的行為完全無任何警覺,私底下在中國內部只要有不利的中國的言論,不斷用另外正向力量來合理化其殘酷的行為,宣傳形成主流意識,對於不當言論一律將它抹除,目的是箝制中國大陸內部的言論自由,讓人民只能相信政府的所作所為,這就夠了,對於世界如何看待中國或是中國共產黨根本無所謂,也無視一個人類該有的言論自由等等的基本人權,導致事實無法忠實呈現,疫情也不誠實申報,讓世界摸不著頭緒,錯失防堵契機
        因此我們要追本溯源給歷史一個真相,中國共產黨箝制言論與隱匿疫情才是造成武漢肺炎世界大流行的罪魁禍首,而 WHO 雖未認清事實真相,但其領導人的消極作為與鄉愿行為卻成為幕後最大推手, 武漢病毒的來源的的確確來自中國武漢地區,一些坊間的報導透漏很可能是在武漢P4實驗室所製造出來,極有可能是管理不慎造成病毒洩漏,從中國軍方接管武漢P4實驗室來看,就能嗅出一些蛛絲馬跡,也是最合理的解釋.

2020年3月4日 星期三

用 grub2 boot loader 啟動 LMDE4

LMDE4 是 Linux Mint Debian edition 4的縮寫, 可以上官網去下載 iso 檔(https://linuxmint.com/rel_debbie.php), 開機時用 grub2 啟動作業系統, 只要修改 grub.cfg, 針對 Debian linux 只要修改啟動選項加入 boot=live 及 findiso(相較於  Ubuntu Linux使用的啟動選項是 boot=casper 及 iso-scan/filename), 就能直接啟動 iso 封裝的 linux , 例如以下範例, 加入一個啟動項目(menuentry):
menuentry "Boot LMDE4 iso" {
    set root=(hd0,2)
    set iso=/boot/lmde4.iso
    loopback loop $iso
    linux (loop)/live/vmlinuz    boot=live     findiso=$iso
    initrd (loop)/live/initrd.lz
}

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...