Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: 关于SPDZ2k协议的实现问题 #967

Open
c-doubley opened this issue Jan 11, 2025 · 10 comments
Open

[Feature]: 关于SPDZ2k协议的实现问题 #967

c-doubley opened this issue Jan 11, 2025 · 10 comments

Comments

@c-doubley
Copy link

Feature Request Type

Build/Install

Have you searched existing issues?

Yes

Is your feature request related to a problem?

spdz2k中的测试实例化有两种,其中makeMpcSpdz2kProtocol使用了beaver_tinyot,但是关注到里面也有TrustedParty,所以使用tinyot版本的spdz2k是否也是用了可信第三方来生成乘法三元组?或者是在其他地方有一部分使用了可信第三方?比如种子分发阶段等。

Describe features you want to add to SPU

A clear and concise description of what you want to happen.

Describe features you want to add to SPU

A clear and concise description of any alternative solutions or features you've considered.

@deadlywing
Copy link
Contributor

从实现上,tinyot是MPC based beaver的一种具体实现,而TFP则是假设有可信第三方;

所以使用tinyot版本的spdz2k是否也是用了可信第三方来生成乘法三元组?或者是在其他地方有一部分使用了可信第三方?比如种子分发阶段等

并没有,tinyot版本基于OT/VOLE来实现

@deadlywing
Copy link
Contributor

另外,spdz2k协议当前我们并没有持续维护的打算,所以里面的实现很多都比较naive,仅供参考~

@c-doubley
Copy link
Author

我看tinyot的初始化里有下面这段代码。p0充当可信方收集了其他参与方的种子

BeaverTinyOt::BeaverTinyOt(std::shared_ptr<yacl::link::Context> lctx)
    : seed_(yacl::crypto::SecureRandSeed()) {
  comm_ = std::make_shared<Communicator>(lctx);
  prg_state_ = std::make_shared<PrgState>(lctx);
  spdz2k_ot_primitives_ = std::make_shared<BasicOTProtocols>(comm_);

  auto buf = yacl::SerializeUint128(seed_);
  std::vector<yacl::Buffer> all_bufs =
      yacl::link::Gather(lctx, buf, 0, "BEAVER_TINY:SYNC_SEEDS");

  if (comm_->getRank() == 0) {
    // Collects seeds from all parties.
    for (size_t rank = 0; rank < comm_->getWorldSize(); ++rank) {
      PrgSeed seed = yacl::DeserializeUint128(all_bufs[rank]);
      tp_.setSeed(rank, comm_->getWorldSize(), seed);
    }
  }

因为我们想做一些恶意安全的协议,所以希望可以在spdz2k的基础上做一些改动,目前是不打算将这个协议作为一个稳定协议发布吗?

@deadlywing
Copy link
Contributor

这部分应该是从TFP那边copy过来的,没有仔细检查-。-
你可以check一下,在beaver_tinyot里面,没有使用tp_的地方;

我们目前人力不是很充足,恶意安全协议主要也是学术 only,短期内应该没有对它优化的计划(包括其他kernel的开发);
Anyway,对于一些building block如VOLE等我理解应该可以复用(或者直接用yacl中的实现);

@c-doubley
Copy link
Author

好的,非常感谢您的解答。spu中的VOLE和yacl中的实现有本质区别吗,以及运行效率相差会很大吗?

@deadlywing
Copy link
Contributor

spdz2k里的VOLE是用的cheetah里的ferret of emp 实现;和yacl的ferret运行效率差不多;现在SPU Cheetah默认都使用YACL的ferret或者softspoken实现

@c-doubley
Copy link
Author

好的了解了,非常感谢!

@c-doubley
Copy link
Author

您好,我还有一点关于vole的实现的问题想要提问

    for (size_t i = 0; i < WorldSize; ++i) {
      for (size_t j = 0; j < WorldSize; ++j) {
        if (i == j) {
          continue;
        }

        if (i == rank) {
          auto tmp = voleRecv(field, alpha);
          a.emplace_back(tmp);
        }
        if (j == rank) {
          auto tmp = voleSend(field, x_hat);
          b.emplace_back(tmp);
        }
      }
    }

假如有三个参与方,那么worldsize=3,对于p0来说
p0 在 i = 1, j = 0 和 i = 2, j = 0 时作为发送方,分别发送数据给 p1 和 p2。
p0 在 i = 0, j = 1 和 i = 0, j = 2 时作为接收方,分别从 p1 和 p2 接收数据。
但是从vole的输入上来看看不出来发送方和接收方
voleRecv(field, alpha);从输入上看不知道是发给谁的。
根据BasicOT中的代码

class BasicOTProtocols {
 public:
  explicit BasicOTProtocols(std::shared_ptr<Communicator> conn);

  ~BasicOTProtocols();

  std::unique_ptr<BasicOTProtocols> Fork();

  int Rank() const;

  std::shared_ptr<FerretOT> GetSenderCOT() { return ferret_sender_; }

  std::shared_ptr<FerretOT> GetReceiverCOT() { return ferret_receiver_; }

  void Flush();

 private:
  std::shared_ptr<Communicator> conn_;
  std::shared_ptr<FerretOT> ferret_sender_;
  std::shared_ptr<FerretOT> ferret_receiver_;
};

是不是意味着一开始就确定好了发送方和接收方是谁,那这样的话p0怎么能又和p1做vole又和p1做一次vole,关于那两个参与方之间进行了vole这个问题我不是很理解,我应该去阅读哪个文件呢?

@deadlywing
Copy link
Contributor

Rank 0是特殊的(现在是写死的),具体可以看BasicOTProtocols的构造函数

BasicOTProtocols::BasicOTProtocols(std::shared_ptr<Communicator> conn)
    : conn_(std::move(conn)) {
  SPU_ENFORCE(conn_ != nullptr);
  if (conn_->getRank() == 0) {
    ferret_sender_ = std::make_shared<FerretOT>(conn_, true);
    ferret_receiver_ = std::make_shared<FerretOT>(conn_, false);
  } else {
    ferret_receiver_ = std::make_shared<FerretOT>(conn_, false);
    ferret_sender_ = std::make_shared<FerretOT>(conn_, true);
  }
}

@c-doubley
Copy link
Author

c-doubley commented Jan 15, 2025

感谢您的回答,这段代码我查看了,在我的理解里这段代码是用来初始化的,让p0刚开始先发送后接收,其他参与方先接收后发送,这是为了避免死锁。
我疑惑的地方在于,在我的理解里OT或者VOLE都是两方协议,那么发送方如何确定接收方的呢,尽管有下面的函数

  std::shared_ptr<FerretOT> GetReceiverCOT() { return ferret_receiver_; }

但是我没有找到ferret_receiver_在哪里被更新了。比如下面这段代码

    for (size_t i = 0; i < WorldSize; ++i) {
      for (size_t j = 0; j < WorldSize; ++j) {
        if (i == j) {
          continue;
        }

        if (i == rank) {
          auto tmp = voleRecv(field, alpha);
          a.emplace_back(tmp);
        }
        if (j == rank) {
          auto tmp = voleSend(field, x_hat);
          b.emplace_back(tmp);
        }
      }
    }

tmp = voleRecv(field, alpha);这里接收的是哪个参与方发送的消息呢,或者说是和那个参与方进行了VOLE协议。尽管可以通过GetReceiverCOT() 查看,但是我不知道在哪里可以更换参与协议的参与方。
假如有三个参与方,对于p0来说,p0 在 i = 0, j = 1 和 i = 0, j = 2 时作为接收方,但是我看不出来这两次执行的voleRecv(field, alpha)有什么区别,为什么就能和不同的参与方执行了VOLE协议呢(这里是我猜的,按逻辑应该是和p1,p2分别做了一次VOLE)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants