天天看点

@[email protected] 源码分析: 单元测试和工程上的一些参考

[email protected]

对于大型项目而言 需要去保证可持续的开发和验证开发的正确性

TDD(Test Driving Development) 代表测试驱动开发

而在palisade中 对于pke的各schema也做了单元测试

这对于大型项目的焦点分离和构造可检验正确性的函数库是很有价值的

可以看看palisade是怎么作单元测试的

@[email protected] 源码分析: 单元测试和工程上的一些参考

pke下的单元测试集中放在 unittest下

使用的gtest单元测试框架

和别的UT库一样 这个库也是使用常用的expect equal等原语为测试的基础

先来分析一下UnitTestEvalSum这一段的单元测试

#include <algorithm>

#include <iostream>

#include <random>

#include <vector>

#include "gtest/gtest.h"

#include "cryptocontext.h"

#include "encoding/encodings.h"

#include "utils/debug.h"

using namespace std;

using namespace lbcrypto;

class UTEvalSum : public ::testing::Test {

 protected:

  void SetUp() {}

  void TearDown() {

    CryptoContextFactory<Poly>::ReleaseAllContexts();

    CryptoContextFactory<DCRTPoly>::ReleaseAllContexts();

  }

 public:

};

int64_t ArbBFVEvalSumPackedArray(std::vector<int64_t> &clearVector,

                                 PlaintextModulus p);

void EvalSumSetup(std::vector<int64_t> &input, int64_t &expectedSum,

                  PlaintextModulus plaintextMod) {

  usint limit = 15;

  PRNG random_engine(1);

  uniform_int_distribution<usint> dist(0, limit);

  auto gen = std::bind(dist, random_engine);

  generate(input.begin(), input.end() - 2, gen);

  expectedSum = std::accumulate(input.begin(), input.end(), 0);

  expectedSum %= plaintextMod;

  int64_t half = int64_t(plaintextMod) / 2;

  if (expectedSum > half) expectedSum -= plaintextMod;

}

//测试的段落所在

TEST_F(UTEvalSum, Test_BFV_EvalSum) {

  usint size = 10;

  std::vector<int64_t> input(size, 0);

  int64_t expectedSum;

  // 直接进行明文的计算

  EvalSumSetup(input, expectedSum, 89);

  // 在加密情况下进行同态计算

  int64_t result = ArbBFVEvalSumPackedArray(input, 89);

  EXPECT_EQ(expectedSum, result);

}

int64_t ArbBFVEvalSumPackedArray(std::vector<int64_t> &clearVector,

                                 PlaintextModulus p) {

  usint m = 22;

  // 按照加密步骤来做这些

  BigInteger modulusP(p);

  BigInteger modulusQ("955263939794561");

  BigInteger squareRootOfRoot("941018665059848");

  BigInteger bigmodulus("80899135611688102162227204937217");

  BigInteger bigroot("77936753846653065954043047918387");

  auto cycloPoly = GetCyclotomicPolynomial<BigVector>(m, modulusQ);

  ChineseRemainderTransformArb<BigVector>::SetCylotomicPolynomial(cycloPoly,

                                                                  modulusQ);

  float stdDev = 4;

  usint batchSize = 8;

  auto params = std::make_shared<ILParams>(m, modulusQ, squareRootOfRoot,

                                           bigmodulus, bigroot);

  // 生成加密参

  EncodingParams encodingParams(std::make_shared<EncodingParamsImpl>(

      p, batchSize, PackedEncoding::GetAutomorphismGenerator(m)));

  PackedEncoding::SetParams(m, encodingParams);

  BigInteger delta(modulusQ.DividedBy(modulusP));

  CryptoContext<Poly> cc = CryptoContextFactory<Poly>::genCryptoContextBFV(

      params, encodingParams, 8, stdDev, delta.ToString());

  cc->Enable(ENCRYPTION);

  cc->Enable(SHE);

  // Initialize the public key containers.

  LPKeyPair<Poly> kp = cc->KeyGen();

  Ciphertext<Poly> ciphertext;

  std::vector<int64_t> vectorOfInts = std::move(clearVector);

  Plaintext intArray = cc->MakePackedPlaintext(vectorOfInts);

  cc->EvalSumKeyGen(kp.secretKey);

  ciphertext = cc->Encrypt(kp.publicKey, intArray);

  auto ciphertextSum = cc->EvalSum(ciphertext, batchSize);

  Plaintext intArrayNew;

  cc->Decrypt(kp.secretKey, ciphertextSum, &intArrayNew);

  return intArrayNew->GetPackedValue()[0];

}

这段代码的思路还是比较容易理解的

首先先制定一个明文的计算函数以供验证 然后ArbBFVEvalSumPackedArray进行实际的

加密情况下的运算 在Test_F中制定加密的参数 并加以验证

看起来palisade的测试步骤是基于产品效果,大步调的,也就是对出品函数级别的单元测试

因为在这个目录中鲜有发现将分离点放在小的配件方法上的

也就是说这个项目整体是基于较完备的规划 在公有util和脚手架已经能够符合要求了的情况下进行构建的

这种情况下 就能够猜想palisade开源团队内部可能的分工是较为内聚的,层次级的

也就是说是分级的开发目标 逐步顺下去。 如先进行通用的数学类型,精密数学后端,再进行格密码层,基础分布生成层,再往上构造elementParam等类型的封装。

在这一级的分工上,应是由一批人专门负责一个schema的开发和issue。而测试人员似乎就由负责开发的人担任 因为这样的测试非常的轻量,似乎一开始就是以这样的目标来构建测试体系的。

@[email protected] 源码分析: 单元测试和工程上的一些参考
@[email protected] 源码分析: 单元测试和工程上的一些参考

Commit的一次小高峰发生在2020 从commit message上看大部分是进行优化和机制的改动

鲜有是对严重bug的修复 可见进入迭代期时palisade是以一种较为健康和良序的方式来进行新周期的运作的 可以猜测其开发人员水准或者较高 以及本身对commit的步调就把握的较好 使得 "heavy development" 能够忙中不乱

@[email protected] 源码分析: 单元测试和工程上的一些参考

从分支来看 这个库现在也仍在保留着相当多的改动和优化 既有针对node.js的特定优化(指node.js的对c/c++ 的 raw调用), 亦有一些改良性的改动还未被正式接纳

@[email protected] 源码分析: 单元测试和工程上的一些参考

而不同类型的改动以不同的前缀进行标识 这种情况下 在频繁提交的时候也许是由不同的人负责不同阶段和类型的代码审核

@[email protected] 源码分析: 单元测试和工程上的一些参考

作为一个已历6141次commit的开源项目而言 其构建团队的执著和开发过程中严谨的文档和积极有效的测试都是需要受到考验的 而正像上面那一段所展现出来的 这个社区的生态还是非常不错的 有很多的commit提交者都不是核心人员 在这种阶段上 很难想象测试是由一批独立个人负责的 这正是可以践行 "开发者构建属于自己的轻量级测试" 的方法

这个代码软件工程上的意义也非常值得探索和挖掘 大规模的项目构建总是伴随着很多的问题 而很多的事情既需要用经验来填补空白,也需要一些巧妙的规则设计,这样的分寸要把握的正好才行。

继续阅读