您正在查看: surou 发布的文章

MPC安全多方计算

隐私问题是目前区块链技术所要解决的其中一重要问题,隐私保护不仅在区块链领域成为了重点研究内容之一,而且近几年不断出现的数据隐私泄露问题,也让公众对隐私保护有日益深入的认知。

PlatON基于安全多方计算密码学算法实现一种隐私合约的解决方案,主要思想是将隐私计算算法通过合约进行发布,并由隐私保护需求的数据提供方和计算节点配合执行MPC协议,以实现数据的协同计算。

安全多方计算介绍

安全多方计算,英文全称为Secure Multi-Party Computation,简称MPC。它指的是用户在无需进行数据归集的情况下,完成数据协同计算,同时保护数据所有方的原始数据隐私。具体来说,有n个计算参与方,分别持有私有数据 共同计算既定函数

计算完成,得到正确计算结果y,且参与各方除了自己的输入数据和输出结果外,无法获知任何额外有效信息。
MPC协议满足的基本性质是:

  • 输入隐私性: 协议执行过程中的中间数据不会泄露双方原始数据的相关信息;
  • 健壮性: 协议执行过程中,参与方不会输出不正确的结果。

构建通用MPC协议的典型范式之一是基于姚期智提出的针对两方计算的加密电路方法,并由Beaver、Micali 和Rogaway 进一步扩展到多方计算。目前MPC相关的协议或算法有Garbled Circuit、Oblivious Transfer、Secret Sharing、BGW、GMW、BMR等,同时工程实现上也有如SPDZ、LEGO、Sharemind、Fairplay等不同的安全多方计算框架出来。典型的安全两方计算所使用的协议为加密电路(Garbled Circuit, GC)和不经意传输(Oblivious Transfer, OT)。

Garbled Circuit

关于Garbled Circuit原理,可进一步参考此页面所做的解释。

目前已经有很多对加密电路进行优化的方案。包括Free-XOR 技术,这意味着XOR 门几乎无需加密,row reduction 和half gate技术,将每个AND门所需要的密文从4个降为2个。最近使用认证加密电路的研究结果实现了针对两方和多方恶意敌手模型下非常有效的协议。

若有兴趣对Garbled Circuit的理论知识作更深入的了解,可参考此论文。

Oblivious Transfer

Oblivious Transfer,中文称为不经意传输,通常简写为OT,它指的是发送者从一个值集合中向接收者发送单个值的问题,这里发送者无法知道发送的是哪一个值,而且接收者也不能获知除了接收值之外的其它任何值。形式化描述为:发送者有由N个值组成的集合,接收者有索引协议执行完成,接收者只知道不知道,这里并且发送者不知道i,这称为1-out-of-N Oblivious Transfer。

对于N=2,即为1-out-of-2 Oblivious Transfer,接收者在计算加密电路之前,首先根据自己的输入数据获得与之对应的标签,由于标签是发送者定义的,因此接收者与发送者之间执行此OT协议。接收者按照其输入的每个比特,以σ=0/1为输入,发送者以标签m0、m1为输入。协议执行完成,接收者获得标签mσ。图7为1-out-of-2 OT的示意图。协议执行的过程中,满足以下性质:

  • 发送者不可获知接收者选择的是哪个标签;
  • 接收者无法知道另一个标签

两方安全计算的工作原理

两方计算的实现过程为:

两个计算参与方Alice和Bob,想共同计算
这里s为Alice所拥有的数据,t为Bob所拥有的数据,f为计算逻辑。首先,Alice将f转换为相应的布尔电路C,其中C的每个门都有一个真值表表示门的输入输出。然后,Alice对真值表进行加密处理,得到加密电路同时,Alice也对其输入进行加密,然后将加密后的输入与加密电路一同发送给Bob。那么此时Bob就拥有了和Alice的加密输入(对应于Alice输入的标签),但却未被告知Alice的加密过程,因此Bob就无法获知该如何使用自己的输入。这时,Bob通过与Alice之间执行1-out-of-2 Oblivious Transfer协议来获得加密输入(对应于自己输入的标签)。之后,Bob使用两方的加密输入对加密电路逐个门进行解密,获得电路计算结果。

总体分为如下五个步骤,其详细过程解释如下:

1. 布尔电路生成

假设是需要安全计算的函数,将此函数转换为布尔电路满足对任意理论上任意函数均可表示成布尔电路。布尔电路的格式如图1所示:

图2: 函数转换为布尔电路

2. 加密电路生成

Alice将函数转换为布尔电路后,将电路的每个门表示成如图2所示的真值表,接着通过加密这些真值表将转化为加密电路

图3: 将电路中的每个门表示成真值表

PlatON中的安全多方计算

安全多方计算为PlatON实现隐私数据的协同计算提供了根本性的技术手段,在PlatON中结合MPC实现隐私合约,为具有多方参与数据作为输入的应用提供隐私保护,实现真正的隐私计算。

PlatON的两方安全计算架构如下:

PlatON的隐私合约同样支持高级语言编程,但不是编译成庞大的布尔电路文件,而是编译成更高效的LLVM IR字节码,并部署到PlatON网络上,在MPC计算节点内置的MPC虚拟机中以JIT方式执行。隐私合约的输入数据保存在数据节点本地,由数据节点在链下以安全多方计算方式进行隐私计算,并提交计算结果到链上。

PlatON会持续优化当前已实现版本的MPC性能,并实现更先进的MPC协议,在以下几个方面进行改进:

  • 结合同态加密(HE)来降低MPC的通信复杂度
  • 从两方安全计算实现到三方以上的多方计算,以满足更加复杂多样化的应用场景

转载自 https://devdocs.platon.network/docs/zh-CN/Secure_Multi_Party_Computation/

开源代码:https://github.com/PlatONnetwork

HD钱包地址生成JavaScript库

支持的加密货币

BTC,ETH,BCH,SLP,BSV,DASH,LTC,DOGE,EOS,XRP,NAV ,STRAT

开源库地址:https://github.com/lpopo0856/cryptohdwallet

合约推送Action参数为time_point的序列化处理

将时间转换为微妙(uint64)推送,链上会自动转为time_point类型

使用history-tool 同步链上数据到PostgreSQL

由于官方的工具history-tool,对比mongo插件,history-tool效率更高,支持PostgreSQL/RocksDB。并且避免了因为意外写入导致链程序意外退出后,数据脏的麻烦问题,并且pg会修复微分叉的交易,所以今天试用下history-tool方案

编译 fill-pg

主要参考 https://eosio.github.io/history-tools/build-ubuntu-1804.html

安装环境依赖

安装Clang 8 和其他需要的工具

sudo apt update && sudo apt install -y wget gnupg

cd ~
sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -

sudo vi /etc/apt/sources.list
## 文件尾部添加
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main

sudo apt update && sudo apt install -y \
    autoconf2.13        \
    build-essential     \
    bzip2               \
    cargo               \
    clang-8             \
    git                 \
    libgmp-dev          \
    libpq-dev           \
    lld-8               \
    lldb-8              \
    ninja-build         \
    nodejs              \
    npm                 \
    pkg-config          \
    postgresql-server-dev-all \
    python2.7-dev       \
    python3-dev         \
    rustc               \
    zlib1g-dev

sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 100

生成并安装Boost 1.70。调整-j10以匹配您的机器。如果您没有足够的RAM用于所使用的内核数量,则会发生不好的事情

cd ~
wget https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz
tar xf boost_1_70_0.tar.gz
cd boost_1_70_0
./bootstrap.sh
sudo ./b2 toolset=clang -j10 install

生成并安装CMake 3.14.5。调整--parallel=并-j匹配您的机器。如果您没有足够的RAM用于所使用的内核数量,则会发生不好的事情:

cd ~
wget https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5.tar.gz
tar xf cmake-3.14.5.tar.gz
cd cmake-3.14.5
./bootstrap --parallel=10
make -j10
sudo make -j10 install

编译history-tools

cd ~
git clone --recursive https://github.com/EOSIO/history-tools.git
cd history-tools
mkdir build
cd build
cmake -GNinja -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_C_COMPILER=clang-8 ..
git submodule update --init --recursive
bash -c "cd ../src && npm install node-fetch"
ninja

此时查看build目录则生成了fill-pg

配置使用

参考 https://eosio.github.io/history-tools/database-fillers.html
首次运行需要创建所需的表,执行参数--fpg-create
如果肺首次执行,需要清理,则添加参数--fpg-drop --fpg-create
--fill-connect-to 需要连接的state-history-plugin endpoint,默认值127.0.0.1:8080
我们先演示不加其他参数的运行实例,如果需要其他的参数,比如过滤,请查看文档--fill-trx

测试运行的命令行参数如下

export PGUSER=       // PostgreSQL用户名
export PGPASSWORD=   // PostgreSQL密码
export PGDATABASE=   // PostgreSQL数据库名
export PGHOST=       // PostgreSQL访问host
export PGPORT=       // PostgreSQL端口

./fill-pg --fill-connect-to 127.0.0.1:8080 --fpg-create

连接后创建的表如下

表名 介绍
account
account_metadata
action_trace
action_trace_auth_sequence
action_trace_authorization
action_trace_ram_delta
action_trace_v1
block_info
code
contract_index_double
contract_index_long_double
contract_index64
contract_index128
contract_index256
contract_row
contract_table
fill_status
generated_transaction
permission
permission_link
protocol_state
received_block
resource_limits
resource_limits_config
resource_limits_state
resource_usage
transaction_trace

测试

查看端口有没有正常监听

sudo lsof -i -P -n | grep LISTEN

如果想测试history节点ws端口可以使用

wget https://github.com/vi/websocat/releases/download/v1.6.0/websocat_arm-linux-static
mv websocat_amd64-linux-static websocat
./websocat ws://127.0.0.1:8080/

注意

目前history-tools方案还处于试验阶段,目前测试遇到问题
https://github.com/EOSIO/history-tools/issues/103
等待后面有时间再继续跟进

根据公钥生成账户名

/**
 * @module AccountName
 */
import * as bs58 from 'bs58';

import * as Long from 'long';

const { PublicKey } = require('./ecc');

/**
    Hashes a public key to a valid FIOIO account name.
    @arg {string} pubkey
    @return {string} valid FIOIO account name
*/
export function accountHash(pubkey : string) : string {
    if(!PublicKey.isValid(pubkey, 'EOS')) {
        throw new TypeError('invalid public key');
    }

    pubkey = pubkey.substring('EOS'.length, pubkey.length);

    const decoded58 = bs58.decode(pubkey);
    const long = shortenKey(decoded58);

    const output = stringFromUInt64T(long);
    return output;
}

function shortenKey(key : [number]) : any {
  var res = Long.fromValue (0, true);
  var temp = Long.fromValue (0, true);
  var toShift = 0;
  var i = 1;
  var len = 0;

  while (len <= 12) {
      //assert(i < 33, "Means the key has > 20 bytes with trailing zeroes...")
      temp = Long.fromValue(key[i], true).and(len == 12 ? 0x0f : 0x1f);
      if (temp == 0) {
          i+=1
          continue
      }
      if (len == 12){
        toShift = 0;
      }
      else{
        toShift = (5 * (12 - len) - 1);
      }
      temp = Long.fromValue(temp, true).shiftLeft(toShift);

      res = Long.fromValue(res, true).or(temp);
      len+=1
      i+=1
  }

  return res;
}

function stringFromUInt64T(temp : any) : string{
  var charmap = ".12345abcdefghijklmnopqrstuvwxyz".split('');

  var str = new Array(13);
  str[12] = charmap[Long.fromValue(temp, true).and(0x0f)];

  temp = Long.fromValue(temp, true).shiftRight(4);
  for (var i = 1; i <= 12; i++) {
      var c = charmap[Long.fromValue(temp, true).and(0x1f)];
      str[12 - i] = c;
      temp = Long.fromValue(temp, true).shiftRight(5);
  }
  var result = str.join('');
  if (result.length > 12) {
      result = result.substring(0, 12);
  }
  return result;
}

测试

describe('accountname', () => {
    it('matches', () => {
        const samplekey = "EOS7isxEua78KPVbGzKemH4nj2bWE52gqj8Hkac3tc7jKNvpfWzYS";
        const accountHash = AccountName.accountHash(samplekey);
        expect(accountHash).toEqual('p4hc54ppiofx');
    })
})