text: // Copyright (c) 2014-2020, The LibertyChain Project
  // 
  // All rights reserved.
  // 
  // Redistribution and use in source and binary forms, with or without modification, are
  // permitted provided that the following conditions are met:
  // 
  // 1. Redistributions of source code must retain the above copyright notice, this list of
  //    conditions and the following disclaimer.
  // 
  // 2. Redistributions in binary form must reproduce the above copyright notice, this list
  //    of conditions and the following disclaimer in the documentation and/or other
  //    materials provided with the distribution.
  // 
  // 3. Neither the name of the copyright holder nor the names of its contributors may be
  //    used to endorse or promote products derived from this software without specific
  //    prior written permission.
  // 
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  // 
  // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
  
  #pragma once
  
  #include "ringct/rctSigs.h"
  #include "ringct/rctTypes.h"
  #include "device/device.hpp"
  
  using namespace rct;
  
  template<size_t a_N, size_t a_T, size_t a_w>
  class test_sig_mlsag
  {
      public:
          static const size_t loop_count = 1000;
          static const size_t N = a_N;
          static const size_t T = a_T;
          static const size_t w = a_w;
  
          bool init()
          {
              pubs.reserve(N);
              pubs.resize(N);
  
              r = keyV(w); // M[l[u]] = Com(0,r[u])
  
              a = keyV(w); // P[l[u]] = Com(a[u],s[u])
              s = keyV(w);
  
              Q = keyV(T); // Q[j] = Com(b[j],t[j])
              b = keyV(T);
              t = keyV(T);
  
              // Random keys
              key temp;
              for (size_t k = 0; k < N; k++)
              {
                  skpkGen(temp,pubs[k].dest);
                  skpkGen(temp,pubs[k].mask);
              }
  
              // Signing and commitment keys (assumes fixed signing indices 0,1,...,w-1 for this test)
              // TODO: random signing indices
              C_offsets = keyV(w); // P[l[u]] - C_offsets[u] = Com(0,s[u]-s1[u])
              s1 = keyV(w);
              key a_sum = zero();
              key s1_sum = zero();
              messages = keyV(w);
              for (size_t u = 0; u < w; u++)
              {
                  skpkGen(r[u],pubs[u].dest); // M[u] = Com(0,r[u])
  
                  a[u] = skGen(); // P[u] = Com(a[u],s[u])
                  s[u] = skGen();
                  addKeys2(pubs[u].mask,s[u],a[u],H);
  
                  s1[u] = skGen(); // C_offsets[u] = Com(a[u],s1[u])
                  addKeys2(C_offsets[u],s1[u],a[u],H);
  
                  sc_add(a_sum.bytes,a_sum.bytes,a[u].bytes);
                  sc_add(s1_sum.bytes,s1_sum.bytes,s1[u].bytes);
  
                  messages[u] = skGen();
              }
  
              // Outputs
              key b_sum = zero();
              key t_sum = zero();
              for (size_t j = 0; j < T-1; j++)
              {
                  b[j] = skGen(); // Q[j] = Com(b[j],t[j])
                  t[j] = skGen();
                  addKeys2(Q[j],t[j],b[j],H);
  
                  sc_add(b_sum.bytes,b_sum.bytes,b[j].bytes);
                  sc_add(t_sum.bytes,t_sum.bytes,t[j].bytes);
              }
              // Value/mask balance for Q[T-1]
              sc_sub(b[T-1].bytes,a_sum.bytes,b_sum.bytes);
              sc_sub(t[T-1].bytes,s1_sum.bytes,t_sum.bytes);
              addKeys2(Q[T-1],t[T-1],b[T-1],H);
  
              // Build proofs
              sigs.reserve(w);
              sigs.resize(0);
              ctkey sk;
              for (size_t u = 0; u < w; u++)
              {
                  sk.dest = r[u];
                  sk.mask = s[u];
  
                  sigs.push_back(proveRctMGSimple(messages[u],pubs,sk,s1[u],C_offsets[u],NULL,NULL,u,hw::get_device("default")));
              }
  
              return true;
          }
  
          bool test()
          {
              for (size_t u = 0; u < w; u++)
              {
                  if (!verRctMGSimple(messages[u],sigs[u],pubs,C_offsets[u]))
                  {
                      return false;
                  }
              }
  
              // Check balanace
              std::vector<MultiexpData> balance;
              balance.reserve(w + T);
              balance.resize(0);
              key ZERO = zero();
              key ONE = identity();
              key MINUS_ONE;
              sc_sub(MINUS_ONE.bytes,ZERO.bytes,ONE.bytes);
              for (size_t u = 0; u < w; u++)
              {
                  balance.push_back({ONE,C_offsets[u]});
              }
              for (size_t j = 0; j < T; j++)
              {
                  balance.push_back({MINUS_ONE,Q[j]});
              }
              if (!(straus(balance) == ONE)) // group identity
              {
                  return false;
              }
  
              return true;
          }
  
      private:
          ctkeyV pubs;
          keyV Q;
          keyV r;
          keyV s;
          keyV s1;
          keyV t;
          keyV a;
          keyV b;
          keyV C_offsets;
          keyV messages;
          std::vector<mgSig> sigs;
  };
  