如何透過 Private Transaction 操作 Hyperledger Besu 私密智能合約

KevinLee
·
·
IPFS
·

企業應用案例演示

前言

Private Transaction(私密交易)是 Hyperledger Besu 重要的技術環節。BSOS 曾經在另一篇文章中,提到如何操作 Hyperledger Besu 的 Private Raw Transaction 以完成一筆 Besu 聯盟鏈的私密交易。不過,實務上我們該如何運用 Besu private transaction 來完成一特定的應用呢?在 Besu 官方文件有以下敘述:

Private transactions either deploy contracts or call contract functions. Ether transfer transactions cannot be private.

Private transaction 在 Besu 中以發佈和呼叫智能合約的方式來進行使用,透過私交易發佈的智能合約稱為私密智能合約 (private smart contract)。根據私交易的特性,交易內容只有參與方節點可以看見交易內容與隱私資訊。

發起一筆私交易時,private transaction model 中的 Payload 參數是一組編碼後 16 進位的字串,它定義對某個智能合約做某種操作。本文將詳細說明如何產生 Payload,並透過範例實際演示如何操作 Besu 的私密智能合約來完成一特定應用。

釐清基本知識

在深入探討私密智能合約前,我們先快速介紹一些基本知識:

  1. Ethereum 智能合約
  2. 智能合約範例:Ballot
  3. Besu 私密智能合約與一般合約有什麼差別?
  4. Besu private transaction 基本參數設定

Ethereum 智能合約

關於智能合約的介紹,網路上已有相當多的資料,本文假設讀者對 Ethereum 智能合約已有基本概念,不加以贅述。以下節錄一段維基百科的內容:

智能合約(英語:Smart contract;智能合同)是一種特殊協議,在區塊鏈內製定合約時使用,當中內含了程式碼函式 (Function),亦能與其他合約進行互動、做決策、儲存資料及傳送以太幣等功能

使用者可透過智能合約提供的 function 操作區塊鏈,我們將 function 分成三大類:

  1. 發佈智能合約
  2. 將資料寫入智能合約
  3. 讀取智能合約的資料

上述發佈及寫入 function 執行後會改變區塊鏈的狀態 (state),在 Ethereum 中需透過 transaction 實現,並支付手續費 (gas)。讀取智能合約的資料不需手續費,可透過客戶端工具 (如 Truffleweb3.jsgeth… 等),或直接使用 Ethereum 提供的 RPC API: eth_call 實作。

智能合約範例:Ballot

下一段我們會利用 Ballot 這張智能合約,實際演示如何操作 Besu 的私密智能合約,因此這裡我們先介紹一下 Ballot。

本文使用 Remix 提供的 Ballot 作為範例,開發語言為 Solidity,該合約的內容與投票相關。以下直接複製 Remix 中程式碼:

pragma solidity >=0.4.22 <0.7.0;
/** 
 * @title Ballot
 * @dev Implements voting process along with vote delegation
 */
contract Ballot {
   
    struct Voter {
        uint weight; // weight is accumulated by delegation
        bool voted;  // if true, that person already voted
        address delegate; // person delegated to
        uint vote;   // index of the voted proposal
    }
struct Proposal {
        // If you can limit the length to a certain number of bytes, 
        // always use one of bytes1 to bytes32 because they are much cheaper
        bytes32 name;   // short name (up to 32 bytes)
        uint voteCount; // number of accumulated votes
    }
address public chairperson;
mapping(address => Voter) public voters;
Proposal[] public proposals;
/** 
     * @dev Create a new ballot to choose one of 'proposalNames'.
     * @param proposalNames names of proposals
     */
    constructor(bytes32[] memory proposalNames) public {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;
for (uint i = 0; i < proposalNames.length; i++) {
            // 'Proposal({...})' creates a temporary
            // Proposal object and 'proposals.push(...)'
            // appends it to the end of 'proposals'.
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
        }
    }
    
    /** 
     * @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
     * @param voter address of voter
     */
    function giveRightToVote(address voter) public {
        require(
            msg.sender == chairperson,
            "Only chairperson can give right to vote."
        );
        require(
            !voters[voter].voted,
            "The voter already voted."
        );
        require(voters[voter].weight == 0);
        voters[voter].weight = 1;
    }
/**
     * @dev Delegate your vote to the voter 'to'.
     * @param to address to which vote is delegated
     */
    function delegate(address to) public {
        Voter storage sender = voters[msg.sender];
        require(!sender.voted, "You already voted.");
        require(to != msg.sender, "Self-delegation is disallowed.");
while (voters[to].delegate != address(0)) {
            to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
            require(to != msg.sender, "Found loop in delegation.");
        }
        sender.voted = true;
        sender.delegate = to;
        Voter storage delegate_ = voters[to];
        if (delegate_.voted) {
            // If the delegate already voted,
            // directly add to the number of votes
            proposals[delegate_.vote].voteCount += sender.weight;
        } else {
            // If the delegate did not vote yet,
            // add to her weight.
            delegate_.weight += sender.weight;
        }
    }
/**
     * @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
     * @param proposal index of proposal in the proposals array
     */
    function vote(uint proposal) public {
        Voter storage sender = voters[msg.sender];
        require(sender.weight != 0, "Has no right to vote");
        require(!sender.voted, "Already voted.");
        sender.voted = true;
        sender.vote = proposal;
// If 'proposal' is out of the range of the array,
        // this will throw automatically and revert all
        // changes.
        proposals[proposal].voteCount += sender.weight;
    }
/** 
     * @dev Computes the winning proposal taking all previous votes into account.
     * @return winningProposal_ index of winning proposal in the proposals array
     */
    function winningProposal() public view
            returns (uint winningProposal_)
    {
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposal_ = p;
            }
        }
    }
/** 
     * @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
     * @return winnerName_ the name of the winner
     */
    function winnerName() public view
            returns (bytes32 winnerName_)
    {
        winnerName_ = proposals[winningProposal()].name;
    }
}

我們可以將 Ballot 智能合約所有 function 分成三大類

1. 發佈智能合約

  • constructor:合約初始化建構,匯入候選人名單。

2. 將資料寫入智能合約

  • giveRightToVote:投票發起人賦予某 account 投票權
  • delegate:將投票權利轉移給其他 account
  • vote:投票

3. 讀取智能合約的資料

  • winningProposal:查看勝選者編號
  • winnerName:查看勝選者姓名

為了後續對智能合約操作,我們需取得智能合約的 ABI 與 Bytecode,有多種方式可以取得:

  • 透過 solc 編譯
  • 透過 Truffle build
  • 這邊我們就直接使用 Remix 取得 ABI 與 Bytecode 的功能。

取得 ABI 與 Bytecode 並定義參數:

const contractAbi = "[{\"constant\":false,\"inputs\":[{\"name\":\"proposal\",\"type\":\"uint256\"}],\"name\":\"vote\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"proposals\",\"outputs\":[{\"name\":\"name\",\"type\":\"bytes32\"},{\"name\":\"voteCount\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"chairperson\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"to\",\"type\":\"address\"}],\"name\":\"delegate\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"winningProposal\",\"outputs\":[{\"name\":\"winningProposal_\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"voter\",\"type\":\"address\"}],\"name\":\"giveRightToVote\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"voters\",\"outputs\":[{\"name\":\"weight\",\"type\":\"uint256\"},{\"name\":\"voted\",\"type\":\"bool\"},{\"name\":\"delegate\",\"type\":\"address\"},{\"name\":\"vote\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"winnerName\",\"outputs\":[{\"name\":\"winnerName_\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"proposalNames\",\"type\":\"bytes32[]\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
const byteCode = "0x608060405234801561001057600080fd5b50604051610e81380380610e818339810180604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185602082028301116401000000008211171561007e57600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060018060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555060008090505b81518110156101bb5760026040805190810160405280848481518110151561015857fe5b90602001906020020151815260200160008152509080600181540180825580915050906001820390600052602060002090600202016000909192909190915060008201518160000155602082015181600101555050508080600101915050610134565b5050610cb5806101cc6000396000f3fe608060405260043610610088576000357c0100000000000000000000000000000000000000000000000000000000900480630121b93f1461008d578063013cf08b146100c85780632e4176cf1461011e5780635c19a95c14610175578063609ff1bd146101c65780639e7b8d61146101f1578063a3ec138d14610242578063e2ba53f0146102ec575b600080fd5b34801561009957600080fd5b506100c6600480360360208110156100b057600080fd5b8101908080359060200190929190505050610317565b005b3480156100d457600080fd5b50610101600480360360208110156100eb57600080fd5b81019080803590602001909291905050506104ba565b604051808381526020018281526020019250505060405180910390f35b34801561012a57600080fd5b506101336104ed565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561018157600080fd5b506101c46004803603602081101561019857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610512565b005b3480156101d257600080fd5b506101db610938565b6040518082815260200191505060405180910390f35b3480156101fd57600080fd5b506102406004803603602081101561021457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109b3565b005b34801561024e57600080fd5b506102916004803603602081101561026557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bfd565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b3480156102f857600080fd5b50610301610c5a565b6040518082815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154141515156103d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f486173206e6f20726967687420746f20766f746500000000000000000000000081525060200191505060405180910390fd5b8060010160009054906101000a900460ff1615151561045e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f416c726561647920766f7465642e00000000000000000000000000000000000081525060200191505060405180910390fd5b60018160010160006101000a81548160ff021916908315150217905550818160020181905550806000015460028381548110151561049857fe5b9060005260206000209060020201600101600082825401925050819055505050565b6002818154811015156104c957fe5b90600052602060002090600202016000915090508060000154908060010154905082565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161515156105dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f596f7520616c726561647920766f7465642e000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610680576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e000081525060200191505060405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561082757600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610822576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f466f756e64206c6f6f7020696e2064656c65676174696f6e2e0000000000000081525060200191505060405180910390fd5b610681565b60018160010160006101000a81548160ff021916908315150217905550818160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161561091c578160000154600282600201548154811015156108f957fe5b906000526020600020906002020160010160008282540192505081905550610933565b816000015481600001600082825401925050819055505b505050565b6000806000905060008090505b6002805490508110156109ae578160028281548110151561096257fe5b90600052602060002090600202016001015411156109a15760028181548110151561098957fe5b90600052602060002090600202016001015491508092505b8080600101915050610945565b505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001807f4f6e6c79206368616972706572736f6e2063616e20676976652072696768742081526020017f746f20766f74652e00000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff16151515610b62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f54686520766f74657220616c726561647920766f7465642e000000000000000081525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154141515610bb357600080fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555050565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b60006002610c66610938565b815481101515610c7257fe5b90600052602060002090600202016000015490509056fea165627a7a7230582072f4679318cf41d402411ff4e56aa9084b22a14afb40f44471d4bd73c400d95b0029"

此智能合約非常適合作為私密智能合約的範例:假設今天有一個由多家企業組成的 Besu 聯盟鏈,當中每個節點代表一間公司。某間公司可發起一場投票,並限定只有聯盟鏈內特定公司能參與,且投票內容與結果不可以被聯盟鏈內外其他公司看見。這個場景需求下,就可以運用 Ballot + 私密交易來實現。

Besu 私密智能合約與一般智能合約有什麼差別?

與一般智能合約不同的是,發佈私密合約時會指定參與方節點,只有參與方節點可以操作智能合約並進行讀寫。

參與方節點可透過私密智能合約的 function 對該合約進行操作。其中,涉及寫入的操作,因為會改變區塊鏈的狀態,即為一私密交易的操作;而涉及讀取的操作,則透過 Besu 提供的 RPC API: priv_call 來操作即可。

目前網路上 Besu private contract 相關資訊較少,此教學影片透過 web3js-eea 搭配 Truffle 操作智能合約。然而我們在前一篇文章已分析,這樣的做法不論彈性、安全性與擴展性是難以被企業接受的。我們依然秉持將所有私密智能合約操作邏輯模組化的原則,以利導入企業架構中。

Besu Private Transaction 基本參數設定

前一篇文章中已說明 private transaction 需要的參數,我們就不再贅述。以下直接進行參數與連線設定:

import "github.com/bsostech/go-besu/privacy"
import "github.com/bsostech/go-besu/types"
...   
privateFromString := "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="
privateFor1String := "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="
privateFor2String := "k2zXEin4Ip/qBGlRkJejnGWdP9cjkK+DAvKNW31L2C8="
privateFrom, _ := privacy.ToPublicKey(privateFromString)
privateFor1, _ := privacy.ToPublicKey(privateFor1String)
privateFor2, _ := privacy.ToPublicKey(privateFor2String)
privateFor := [][]byte{privateFor1, privateFor2}
participants := []*privacy.PublicKey{&privateFrom, &privateFor1, &privateFor2}
ethClient := ethclient.NewClient(rpcClient)
privateKey, _ := crypto.HexToECDSA("8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63")
publicKey := privateKey.Public()
publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
gasLimit := uint64(3000000)
networkID, _ := ethClient.NetworkID(context.TODO())
rpcClient, _ := rpc.Dial("http://host:port")
priv := privacy.NewPrivacy(rpcClient)
rootPrivacyGroup := priv.FindRootPrivacyGroup(participants)
privateNonce, _ := priv.PrivateNonce(fromAddress, rootPrivacyGroup)
parsedABI, _ := abi.JSON(strings.NewReader(contractAbi))

接下來,我們就正式展開範例實作,演示如何操作 Besu 私密智能合約。

在 Besu 發佈一張私密智能合約

以下是 private transaction model

// Transaction .
type Transaction struct {
 Data txdata
}
type txdata struct {
 AccountNonce uint64          `json:"nonce"    gencodec:"required"`
 Price        *big.Int        `json:"gasPrice" gencodec:"required"`
 GasLimit     uint64          `json:"gas"      gencodec:"required"`
 Recipient    *common.Address `json:"to"       rlp:"nil"` // nil means contract creation
 Amount       *big.Int        `json:"value"    gencodec:"required"`
 Payload      []byte          `json:"input"    gencodec:"required"`
 V *big.Int `json:"v" gencodec:"required"`
 R *big.Int `json:"r" gencodec:"required"`
 S *big.Int `json:"s" gencodec:"required"`
 PrivateFrom []byte   `json:"private_from"    gencodec:"required"`
 PrivateFor  [][]byte `json:"private_for"    gencodec:"required"`
 Restriction string
}

發佈私密智能合需透過 private transaction 實現,重點在於如何產生 private transaction model 中的 Payload (input data)。

Payload 定義這筆 transaction 針對某個智能合約執行某個 function。當 Recipient 為空值時,代表這筆 transaction 是要發佈一個智能合約並執行 constructor function,此時 Payload 要帶入智能合約的 Bytecode 以及constructor function 的參數。以 Ballot 為例,constructor function 在智能合約定義如下:

constructor(bytes32[] memory proposalNames)

由此可見,constructor function 參數是一個 bytes32 array,代表候選人名單。以下是產生發佈 Ballot 智能合約的 Payload 流程:

  1. 定義候選人名單

在智能合約中候選人的投票號碼從 0 開始,以下三位候選人編號分別是 0 號,1 號,與 2 號。

proposalNames := []string{"Alice", "Bob", "Kevin"}

2. 型態轉換

智能合約中定義的建構參數是 [32]byte 的 array

names := make([][32]byte, 0, len(proposalNames))
for i := range proposalNames {
	newArg := [32]byte{}
	copy(newArg[:], proposalNames[i])
	names = append(names, newArg)
}

3. 參數打包

將智能合約 function name 與參數打包,發佈合約時的 function name 設為空值。

arguments, _ := parsedABI.Pack("", names)

4. 產生 Payload

發佈智能合約的 payload 是以智能合約 Bytecode 加上打包後的參數代表。成功取得 payload 後,帶入 NewContractCreation 的 data 中,便可成功定義一筆發佈合約的 private transaction。

data := append(common.FromHex(byteCode), arguments...)
fmt.Println(hexutil.Encode(data))
> 0x608060405234801561001057600080fd5b50604051610e81380380610e818339810180604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185602082028301116401000000008211171561007e57600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060018060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555060008090505b81518110156101bb5760026040805190810160405280848481518110151561015857fe5b90602001906020020151815260200160008152509080600181540180825580915050906001820390600052602060002090600202016000909192909190915060008201518160000155602082015181600101555050508080600101915050610134565b5050610cb5806101cc6000396000f3fe608060405260043610610088576000357c0100000000000000000000000000000000000000000000000000000000900480630121b93f1461008d578063013cf08b146100c85780632e4176cf1461011e5780635c19a95c14610175578063609ff1bd146101c65780639e7b8d61146101f1578063a3ec138d14610242578063e2ba53f0146102ec575b600080fd5b34801561009957600080fd5b506100c6600480360360208110156100b057600080fd5b8101908080359060200190929190505050610317565b005b3480156100d457600080fd5b50610101600480360360208110156100eb57600080fd5b81019080803590602001909291905050506104ba565b604051808381526020018281526020019250505060405180910390f35b34801561012a57600080fd5b506101336104ed565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561018157600080fd5b506101c46004803603602081101561019857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610512565b005b3480156101d257600080fd5b506101db610938565b6040518082815260200191505060405180910390f35b3480156101fd57600080fd5b506102406004803603602081101561021457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109b3565b005b34801561024e57600080fd5b506102916004803603602081101561026557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bfd565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b3480156102f857600080fd5b50610301610c5a565b6040518082815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154141515156103d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f486173206e6f20726967687420746f20766f746500000000000000000000000081525060200191505060405180910390fd5b8060010160009054906101000a900460ff1615151561045e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f416c726561647920766f7465642e00000000000000000000000000000000000081525060200191505060405180910390fd5b60018160010160006101000a81548160ff021916908315150217905550818160020181905550806000015460028381548110151561049857fe5b9060005260206000209060020201600101600082825401925050819055505050565b6002818154811015156104c957fe5b90600052602060002090600202016000915090508060000154908060010154905082565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161515156105dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f596f7520616c726561647920766f7465642e000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610680576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e000081525060200191505060405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561082757600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610822576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f466f756e64206c6f6f7020696e2064656c65676174696f6e2e0000000000000081525060200191505060405180910390fd5b610681565b60018160010160006101000a81548160ff021916908315150217905550818160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161561091c578160000154600282600201548154811015156108f957fe5b906000526020600020906002020160010160008282540192505081905550610933565b816000015481600001600082825401925050819055505b505050565b6000806000905060008090505b6002805490508110156109ae578160028281548110151561096257fe5b90600052602060002090600202016001015411156109a15760028181548110151561098957fe5b90600052602060002090600202016001015491508092505b8080600101915050610945565b505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001807f4f6e6c79206368616972706572736f6e2063616e20676976652072696768742081526020017f746f20766f74652e00000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff16151515610b62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f54686520766f74657220616c726561647920766f7465642e000000000000000081525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154141515610bb357600080fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555050565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b60006002610c66610938565b815481101515610c7257fe5b90600052602060002090600202016000015490509056fea165627a7a7230582072f4679318cf41d402411ff4e56aa9084b22a14afb40f44471d4bd73c400d95b002900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003416c696365000000000000000000000000000000000000000000000000000000426f6200000000000000000000000000000000000000000000000000000000004b6576696e000000000000000000000000000000000000000000000000000000

5. 產生 private raw transaction

besutx := types.NewContractCreation(privateNonce, nil, gasLimit, big.NewInt(0), data, privateFrom, privateFor)
besuSignedTx, _ := besutx.SignTx(networkID, privateKey)
besuRawTxData, _ := rlp.EncodeToBytes(besuSignedTx)
for i := range besuRawTxData { // remove redundant dust
    tmp, _ := rlp.EncodeToBytes(besuSignedTx)
    tmp = append(tmp[:1], tmp[i:]...)
    var txSlice []interface{}
    err = rlp.DecodeBytes(tmp, &txSlice)
    if err != nil {
        continue
    }
    if len(txSlice) == 12 { // 12 args in private transaction
        besuRawTxData = tmp
        break
    }
}
fmt.Println(hexutil.Encode(besuRawTxData))

6. 發佈 Ballot 私密智能合約

透過 Besu RPC API: eea_sendRawTransaction 執行 private raw transaction,成功發佈合約上鏈並取得交易序號 (transaction hash)。將此交易序號發給所有參與的節點。

參與節點透過 Besu RPC API: priv_getTransactionReceipt 取得 contract address 並做後續操作;非參與節點則拿不到交易內容。

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "contractAddress": "0x8c9e3cb802d0b3c366c5e8c82ce506c1072deaa4",
    "from": "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73",
    "output": "0x608060405260043610610088576000357c0100000000000000000000000000000000000000000000000000000000900480630121b93f1461008d578063013cf08b146100c85780632e4176cf1461011e5780635c19a95c14610175578063609ff1bd146101c65780639e7b8d61146101f1578063a3ec138d14610242578063e2ba53f0146102ec575b600080fd5b34801561009957600080fd5b506100c6600480360360208110156100b057600080fd5b8101908080359060200190929190505050610317565b005b3480156100d457600080fd5b50610101600480360360208110156100eb57600080fd5b81019080803590602001909291905050506104ba565b604051808381526020018281526020019250505060405180910390f35b34801561012a57600080fd5b506101336104ed565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561018157600080fd5b506101c46004803603602081101561019857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610512565b005b3480156101d257600080fd5b506101db610938565b6040518082815260200191505060405180910390f35b3480156101fd57600080fd5b506102406004803603602081101561021457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109b3565b005b34801561024e57600080fd5b506102916004803603602081101561026557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bfd565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b3480156102f857600080fd5b50610301610c5a565b6040518082815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154141515156103d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f486173206e6f20726967687420746f20766f746500000000000000000000000081525060200191505060405180910390fd5b8060010160009054906101000a900460ff1615151561045e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f416c726561647920766f7465642e00000000000000000000000000000000000081525060200191505060405180910390fd5b60018160010160006101000a81548160ff021916908315150217905550818160020181905550806000015460028381548110151561049857fe5b9060005260206000209060020201600101600082825401925050819055505050565b6002818154811015156104c957fe5b90600052602060002090600202016000915090508060000154908060010154905082565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161515156105dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f596f7520616c726561647920766f7465642e000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610680576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e000081525060200191505060405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561082757600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610822576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f466f756e64206c6f6f7020696e2064656c65676174696f6e2e0000000000000081525060200191505060405180910390fd5b610681565b60018160010160006101000a81548160ff021916908315150217905550818160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161561091c578160000154600282600201548154811015156108f957fe5b906000526020600020906002020160010160008282540192505081905550610933565b816000015481600001600082825401925050819055505b505050565b6000806000905060008090505b6002805490508110156109ae578160028281548110151561096257fe5b90600052602060002090600202016001015411156109a15760028181548110151561098957fe5b90600052602060002090600202016001015491508092505b8080600101915050610945565b505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001807f4f6e6c79206368616972706572736f6e2063616e20676976652072696768742081526020017f746f20766f74652e00000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff16151515610b62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f54686520766f74657220616c726561647920766f7465642e000000000000000081525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154141515610bb357600080fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555050565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b60006002610c66610938565b815481101515610c7257fe5b90600052602060002090600202016000015490509056fea165627a7a7230582072f4679318cf41d402411ff4e56aa9084b22a14afb40f44471d4bd73c400d95b0029",
    "commitmentHash": "0x10e55769d2a867a2a99336d5f32f8ddc059286ec2f276ac333fd9af2e3252b6a",
    "transactionHash": "0x2a2f5c96f453ded3396defbbb1b43f7288f130e254ee346be543410dbb1e18f6",
    "privateFrom": "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=",
    "privateFor": [
      "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=",
      "k2zXEin4Ip/qBGlRkJejnGWdP9cjkK+DAvKNW31L2C8="
    ],
    "status": "0x1",
    "logs": [],
    "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "blockHash": "0x983db7371c3a0bb87764bdbd4fbfc3fae654b55a7d957b2015e069c84dbd7079",
    "blockNumber": "0x4856c",
    "transactionIndex": "0x0"
  }
}

從 receipt 結果我們拿到此私密智能合約的地址為 0x8c9e3cb802d0b3c366c5e8c82ce506c1072deaa4

寫資料到 Besu 私密智能合約

寫資料到 Besu 私密智能合約一樣需透過 private transaction 實現,以下「投票發起人賦予某 account 投票權」及「投票」兩個範例說明如何產生 private transaction model 中的 payload:

1. 投票發起人賦予某 account 投票權

由投票發起人執行 function giveRightToVote,賦予 account 0x90051231a25696Dfd0526f5f69587C61d26F87cF 投票權。

data, _ := parsedABI.Pack("giveRightToVote", common.HexToAddress("0x90051231a25696Dfd0526f5f69587C61d26F87cF"))
fmt.Println(hexutil.Encode(data))

2. 投票

由 account 0x90051231a25696Dfd0526f5f69587C61d26F87cF 執行 function vote,發起投票。若由其他 account 發起交易,由於沒有投票權,將導致交易失敗。

data, _ := parsedABI.Pack("vote", big.NewInt(2)) // 投給 2 號 Kevin
fmt.Println(hexutil.Encode(data))
> 0x0121b93f0000000000000000000000000000000000000000000000000000000000000002

取得 payload 後,帶入 NewTransaction 中的 data,Recipient 填入要操作智能合約的地址 (0x8c9e3cb802d0b3c366c5e8c82ce506c1072deaa4),成功定義一筆 private transaction,進而產生 private raw transaction。

contractAddress := common.HexToAddress("0x8c9e3cb802d0b3c366c5e8c82ce506c1072deaa4")
data, _ := hexutil.Decode("0x0121b93f0000000000000000000000000000000000000000000000000000000000000002")
besutx := types.NewTransaction(privateNonce, &contractAddress, nil, gasLimit, big.NewInt(0), data, privateFrom, privateFor)
besuSignedTx, _ := besutx.SignTx(networkID, privateKey)
besuRawTxData, _ := rlp.EncodeToBytes(besuSignedTx)
for i := range besuRawTxData { // remove redundant dust
    tmp, _ := rlp.EncodeToBytes(besuSignedTx)
    tmp = append(tmp[:1], tmp[i:]...)
    var txSlice []interface{}
    err = rlp.DecodeBytes(tmp, &txSlice)
    if err != nil {
        continue
    }
    if len(txSlice) == 12 { // 12 args in private transaction
        besuRawTxData = tmp
        break
    }
}
fmt.Println(hexutil.Encode(besuRawTxData))

透過 Besu API: eea_sendRawTransaction 執行 private raw transaction,成功發佈合約上鏈並取得交易序號 (transaction hash)。

讀取 Besu 私密智能合約的資料

讀取 Ballot 私密智能合約的資訊,查看目前最高票的候選人。實作流程如下:

1. 取得並定義合約地址:由投票發起人提供合約發佈的交易序號,或直接提供合約地址

contractAddress := common.HexToAddress("0x8c9e3cb802d0b3c366c5e8c82ce506c1072deaa4")

2. 參數打包

本 function 無參數,故只需帶入 function name。

winner, _ := parsedABI.Pack("winnerName")

3. 合約呼叫設定

msg := map[string]interface{}{
	"to":   contractAddress,
	"data": hexutil.Bytes(winner),
}

4. 呼叫私密智能合約

需帶入 privacy group,只有參與節點可查看合約內容。最後我們可以查得目前最標票者。

var result interface{}
rpcClient.CallContext(context.TODO(), &result, "priv_call", rootPrivacyGroup.ID, msg, "latest")
output, _ := hexutil.Decode(result.(string))
log.Println(string(output))
> Kevin

結語

Hyperledger Besu 與 Ethereum 最大不同在於 privacy 與 permission,而 private transaction 又是 privacy 中最重要的一環。前一篇文章我們說明了如何透過模組化的方式產生 private raw transaction,本文進一步分享實務上如何執行 private transaction ,即操作私密智能合約,並使用 Ballot 作為智能合約範例,具體演示私密智能合約的發佈、寫入與讀取。

Private transaction 是聯盟鏈應用中不可或缺的功能,筆者任職的 BSOS 是台灣少數專注在聯盟鏈及企業區塊鏈應用的公司,熟稔各種企業間 private transaction 的應用技術。BSOS 的核心技術 BridgeX 為企業區塊鏈的整合方案,除了國際三大主流聯盟鏈:Fabric、Quorum、Corda 之外,亦完美整合了 Hyperledger Besu,並支援其私密智能合約的操作。使用 BridgeX 的開發者可直接透過 Restful API 對底層區塊鏈進行操作,大幅降低企業導入區塊鏈的時間與技術門檻。

我們將持續關注國際「企業區塊鏈/聯盟鏈」的技術發展,並透過文章與大家分享交流。這塊領域還在快速發展中,歡迎志同道合的朋友們,多多與我們聯繫指教。

參考資料

https://medium.com/p/13a651637fc7

https://besu.hyperledger.org/en/stable/HowTo/Send-Transactions/Creating-Sending-Private-Transactions https://www.youtube.com/watch?v=Menekt6-TEQ

https://remix.ethereum.org/

https://www.trufflesuite.com/

https://daml.com/

https://vyper.readthedocs.io/

CC BY-NC-ND 2.0 授权