返回文章列表

區塊鏈智慧合約無伺服器架構應用

本文探討區塊鏈技術於無伺服器架構的應用,以智慧合約為例,示範如何利用 Solidity 撰寫智慧合約,並使用 Truffle 框架佈署至區塊鏈網路。同時,文章也說明如何透過 WebHook 與智慧合約互動,以及如何使用 Chromeless 自動化瀏覽器操作,整合舊有系統。最後,文章提供最佳化建議和未來展望,並以

區塊鏈 無伺服器架構

本文探討區塊鏈技術在無伺服器架構中的應用,並以智慧合約的實作案例說明整合流程。首先,我們會以 Solidity 語言撰寫一個用於管理手機號碼註冊資訊的智慧合約,並使用 Truffle 框架將其佈署至以太坊區塊鏈網路。接著,示範如何使用 WebHook 與已佈署的智慧合約進行互動,以查詢手機號碼的註冊資訊。此外,本文也介紹如何使用 Chromeless 自動化瀏覽器操作,將舊有的 UI 系統包裝成 OpenFaaS 函式,並與 WebHook 整合,實作更完整的應用場景。最後,我們將探討如何使用 Go 語言實作 Glue 功能,串接不同服務,並利用 Dockerfile 多階段建置最佳化佈署流程。

區塊鏈技術在無伺服器架構中的應用:以智慧合約為例

隨著區塊鏈技術的不斷發展,其在無伺服器架構中的應用也日益受到關注。本文將以智慧合約為例,探討區塊鏈技術在無伺服器架構中的應用,並深入分析相關技術細節。

智慧合約的基本概念

智慧合約是一種執行在區塊鏈上的程式碼,它能夠自動執行特定的任務。智慧合約的執行結果會被記錄在區塊鏈上,因此具有不可篡改的特性。

註冊智慧合約的實作

pragma solidity ^0.5.0;

contract RegistrationRepository {
    event RegistrationFound(string telNo, string bank, string accNo);
    event RegistrationNotFound(string telNo);

    mapping(string => Registration) public registrations;

    struct Registration {
        string bank;
        string accNo;
        address owner;
    }

    function register(string memory telNo, string memory bank, string memory accNo) public {
        registrations[telNo] = Registration(bank, accNo, msg.sender);
        emit Registered(telNo, msg.sender);
    }

    function findByTelNo(string memory telNo) public view returns (address) {
        Registration memory r = registrations[telNo];
        require(r.owner != address(0), "Registration not found");
        emit RegistrationFound(telNo, r.bank, r.accNo);
        return r.owner;
    }
}

內容解密:

此智慧合約用於管理手機號碼的註冊資訊。主要功能包括:

  1. register 函式:將手機號碼與銀行帳戶資訊進行繫結,並記錄擁有者地址。
  2. findByTelNo 函式:根據手機號碼查詢相關的銀行和帳戶資訊。
  3. 事件機制:當查詢到註冊資訊時觸發 RegistrationFound 事件,否則觸發 RegistrationNotFound 事件。

使用 Truffle 框架佈署智慧合約

Truffle 是一個流行的以太坊開發框架,用於編譯、佈署和測試智慧合約。

佈署指令碼範例

var RegistrationRepository = artifacts.require("./RegistrationRepository.sol");

module.exports = function(deployer) {
    deployer.deploy(RegistrationRepository).then(function() {
        RegistrationRepository.deployed().then(function(repo) {
            repo.register("+661234567", "faas", "55700").then();
            repo.register("+661111111", "whisk", "A1234").then();
        });
    });
};

內容解密:

此指令碼用於佈署 RegistrationRepository 智慧合約,並註冊兩個手機號碼,分別對應不同的銀行和帳戶。

區塊鏈網路的設定

本文使用 Parity 客戶端建立以太坊區塊鏈網路。

Parity 客戶端啟動命令

docker run --rm --name=parity_dev -d -p 8545:8545 -p 8180:8180 \
    --network=parse_net \
    --network-alias=blockchain \
    parity/parity:stable-release \
    --geth --chain dev --force-ui \
    --reseal-min-period 0 \
    --jsonrpc-cors http://localhost \
    --jsonrpc-apis all \
    --jsonrpc-interface 0.0.0.0 \
    --jsonrpc-hosts all

內容解密:

此命令啟動一個 Parity 客戶端容器,並組態相關引數以便與其他服務進行互動。

WebHook 如何與區塊鏈互動

WebHook 函式透過呼叫智慧合約來查詢手機號碼的註冊資訊。

Java 程式碼範例

public RegistrationResult lookup(String telNo) throws Exception {
    val repo = ContractRegistry.registrationRepository();
    val receipt = repo.findByTelNo(telNo).send();

    val foundEvents = repo.getRegistrationFoundEvents(receipt);
    if (!foundEvents.isEmpty()) {
        val reg = foundEvents.get(0);
        return new RegistrationResult(reg.bank, reg.accNo);
    } else {
        val notFoundEvents = repo.getRegistrationNotFoundEvents(receipt);
        if (!notFoundEvents.isEmpty()) {
            return null;
        }
    }
    throw new Exception("Lookup does not find any event in receipt.");
}

內容解密:

此函式呼叫 findByTelNo 方法查詢手機號碼的註冊資訊,並根據事件結果傳回相關資訊或丟擲異常。

最佳化建議

  1. 最佳化事件處理:目前的實作中,智慧合約會觸發多個事件。可以考慮最佳化為只觸發一個事件,以提高效率。
  2. 錯誤處理:在 WebHook 函式中,可以增加更完善的錯誤處理機制,以應對各種可能的異常情況。

使用 Chromeless 包裝舊系統

Chromeless 是一個 Node.js 函式庫,用於實作瀏覽器自動化。

建立 hivectl 函式

$ faas new hivectl --lang node

內容解密:

此命令使用 FaaS CLI 建立一個名為 hivectl 的新函式,用於包裝舊有的 UI 系統。

隨著區塊鏈技術和無伺服器架構的不斷成熟,我們可以預見更多的企業將採用這些技術來構建更安全、更高效的應用系統。同時,開發者需要不斷學習和掌握相關技術,以應對日益複雜的技術挑戰。

附錄

系統架構圖

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 系統架構圖

rectangle "呼叫智慧合約" as node1
rectangle "傳回結果" as node2
rectangle "被Chromeless包裝" as node3
rectangle "與WebHook互動" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

圖表翻譯: 此圖表展示了系統的整體架構,包括 WebHook 如何與區塊鏈網路互動,以及如何使用 Chromeless 包裝舊系統並與 WebHook 進行互動。

程式碼最佳實踐

在開發過程中,應遵循最佳實踐來確保程式碼的品質和可維護性。例如,使用清晰的命名規則、新增必要的註解、以及進行充分的測試等。這些做法有助於提高程式碼的可讀性和可靠性。

使用 OpenFaaS 和 Chromeless 自動化頭部 Chrome 瀏覽器操作

簡介

本文將介紹如何利用 OpenFaaS 和 Chromeless 實作自動化頭部 Chrome 瀏覽器操作。我們將建立一個名為 hivectl 的 OpenFaaS 函式,該函式透過 Chromeless 控制頭部 Chrome 瀏覽器,以自動化 HiveMind ERP 系統中的財務帳戶調整操作。

建立 OpenFaaS 函式

首先,我們需要建立一個名為 hivectl 的 OpenFaaS 函式。執行以下命令:

$ faas new hivectl --lang node

該命令將在當前目錄下建立一個名為 hivectl 的資料夾,並包含 OpenFaaS 函式的描述檔案 hivectl.yml

組態 OpenFaaS 函式

以下是 hivectl.yml 的內容:

provider:
  name: faas
  gateway: http://localhost:8080
functions:
  hivectl:
    lang: node
    handler: ./hivectl
    image: chanwit/hivectl:0.4

該組態檔案定義了一個名為 hivectl 的 OpenFaaS 函式,使用 Node.js 語言,處理器為 ./hivectl,並使用 chanwit/hivectl:0.4 映象。

編寫 Chromeless 指令碼

以下是 hivectl/handler.js 的內容:

const { Chromeless } = require('chromeless')

const url = 'http://hivemind/vapps/hmadmin/Accounting/FinancialAccount/FinancialAccountTrans?finAccountId='

module.exports = (content, callback) => {
  async function run(accountId, amount) {
    const chromeless = new Chromeless({
      launchChrome: false,
      cdp: { host: 'chrome', port: 9222, secure: false, closeTab: true }
    })

    const screenshot = await chromeless
      .goto('http://hivemind/Login/logout')
      .click('#TestLoginLink_button')
      .wait('.btn-danger')
      .goto(url + accountId)
      .wait('#AdjustDialog-button')
      .click('#AdjustDialog-button')
      .type(amount, '#AdjustFinancialAccount_amount')
      .mousedown('#select2-AdjustFinancialAccount_reasonEnumId-container')
      .mouseup('#select2-AdjustFinancialAccount_reasonEnumId-container')
      .press(40, 5)
      .press(13)
      .click('#AdjustFinancialAccount_submitButton')
      .screenshot()
      .catch(e => {
        console.log('{"error":"' + e.message + '"}')
        process.exit(1);
      })

    console.log('{"success": "ok", "screenshot":"' + screenshot + '"}')
    await chromeless.end()
  }

  const opt = JSON.parse(content)
  run(opt.accountId, opt.amount).catch(console.error.bind(console))
};

內容解密:

此指令碼使用 Chromeless 控制頭部 Chrome 瀏覽器,以自動化 HiveMind ERP 系統中的財務帳戶調整操作。首先,它導航到登出頁面並點選測試登入連結。然後,它導航到指定的財務帳戶頁面,點選調整按鈕,輸入調整金額,選擇調整原因,並點選提交按鈕。最後,它擷取螢幕並輸出結果。

建置和佈署 OpenFaaS 函式

執行以下命令建置 OpenFaaS 函式:

$ faas build -f ./hivectl.yml

建置完成後,將映象推播到 Docker Hub。

啟動頭部 Chrome 瀏覽器和 HiveMind ERP 系統

執行以下命令啟動頭部 Chrome 瀏覽器:

$ docker run -d --network=parse_net \
  --network-alias=chrome \
  --cap-add=SYS_ADMIN \
  justinribeiro/chrome-headless

執行以下命令啟動 HiveMind ERP 系統:

$ docker run -p 10000:80 \
  -d --network=parse_net \
  --network-alias=hivemind \
  moqui/hivemind

呼叫 OpenFaaS 函式

以下是 WebHook 程式碼,呼叫 hivectl 函式:

public boolean faasAdjust(String txId, String accountId, Double amount) throws Exception {
  val env = System.getenv("FAAS_GATEWAY_SERVICE");
  val faasGatewayService = (env == null ? "http://gateway:8080" : env);
  val JSON = MediaType.parse("application/json; charset=utf-8");
  val client = new OkHttpClient();
  val json = new ObjectMapper().writeValueAsString(new HashMap<String, String>() {{
    put("accountId", accountId);
    put("amount", String.valueOf(amount));
  }});
  val body = RequestBody.create(JSON, json);
  val request = new Request.Builder()
    .url(faasGatewayService + "/function/hivectl")
    .post(body)
    .build();
  val response = client.newCall(request).execute();
  System.out.println(response);
  if (response.code() == 200) {
    val str = response.body().string();
    return true;
  }
  throw new Exception(response.toString());
}

圖表翻譯:

此圖表呈現了整個系統的架構,包括 OpenFaaS、Chromeless、頭部 Chrome 瀏覽器和 HiveMind ERP 系統之間的互動流程。

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖表翻譯:

rectangle "呼叫" as node1
rectangle "控制" as node2
rectangle "操作" as node3
rectangle "傳回" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

圖表翻譯: 此圖表呈現了 OpenFaaS 呼叫 hivectl 函式,hivectl 函式控制頭部 Chrome 瀏覽器,瀏覽器操作 HiveMind ERP 系統,並傳回結果的整個流程。

使用 Go 語言實作 Glue 功能

以下是使用 Go 語言實作的 Glue 功能程式碼:

func main() {
  input := os.Args[1]
  // OpenWhisk params are key/value pairs
  params := map[string]interface{}{}
  err := json.Unmarshal([]byte(input), &params)
  if err != nil {
    fmt.Printf(`{"error":"%s", "input": "%s"}`, err.Error(), string(input))
    os.Exit(-1)
  }
  
  entry := Entry{
    Account: Account{
      Id: params["accountId"].(string),
    },
    Amount: params["amount"].(float64),
  }
  
  jsonValue, err := json.Marshal(entry)
  if err != nil {
    fmt.Printf(`{"error":"%s"}`, err.Error())
    os.Exit(-1)
  }
  
  accountService := os.Getenv("ACCOUNT_SERVICE")
  
  if accountService == "" {
    accountService = "http://accounting:8080/entries"
  }
  
  resp, err := http.Post(accountService, "application/json", bytes.NewBuffer(jsonValue))
  if err != nil {
    fmt.Printf(`{"error":"%s"}`, err.Error())
    os.Exit(-1)
  }
  
  if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
    fmt.Println(`{"success": "ok"}`)
    os.Exit(0)
  }
  
  fmt.Printf(`{"error": "%s"}`, resp.Status)
}

Dockerfile 多階段建置

以下是 Dockerfile 的內容:

# Stage 0
FROM golang:1.8.5-alpine3.6
WORKDIR /go/src/app
COPY account_ctl.go .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-extldflags "-static"' -o exec account_ctl.go

# Stage 1
FROM openwhisk/dockerskeleton
ENV FLASK_PROXY_PORT 8080
COPY --from=0 /go/src/app/exec /action/
RUN chmod +x /action/exec
CMD ["/bin/bash", "-c", "cd actionProxy python -u actionproxy.py"]

多階段建置流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 多階段建置流程

rectangle "複製" as node1
rectangle "建置" as node2
rectangle "設定環境" as node3

node1 --> node2
node2 --> node3

@enduml

圖表翻譯: 此圖表呈現了多階段建置的流程,包括原始碼複製、靜態二進位制檔案建置、以及最終映象的建立。