I am using https://github.com/HareInWeed/gec for point addition on the secp256k1. The code below adds two points on secp256k1 curve and displays the results.
#include <gec/utils/macros.hpp>
#include <gec/bigint.hpp>
#include <gec/curve.hpp>
#include <iostream>
// g++ testing.cpp -O3
using namespace gec::bigint::literal; // use the bigint literal
// use uint64 x 4 to store a single element on finite field
using Bigint256 = gec::bigint::ArrayBE<uint64_t, 4>;
// define parameters required by montgomery multiplication:
GEC_DEF_GLOBAL(MOD, Bigint256, // cardinality of finite field
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_int);
constexpr Bigint256::LimbT MOD_P = // -MOD^-1 mod 2^64
0xd838091dd2253531ull;
GEC_DEF_GLOBAL(RR, Bigint256, // 2^512 mod MOD
0x01000007a2000e90a1_int);
GEC_DEF_GLOBAL(ONE_R, Bigint256, // 2^256 mod MOD
0x1000003d1_int);
// define the finite field type
using Field = GEC_BASE_FIELD(Bigint256, MOD, MOD_P, RR, ONE_R);
// define parameters required by montgomery multiplication:
GEC_DEF_GLOBAL(CARD, Bigint256, // cardinality of the elliptic curve
0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141_int);
constexpr Bigint256::LimbT CARD_P = // -CARD^-1 mod 2^64
0x4b0dff665588b13full;
GEC_DEF_GLOBAL(CARD_RR, Bigint256, // 2^512 mod CARD
0x9d671cd581c69bc5e697f5e45bcd07c6741496c20e7cf878896cf21467d7d140_int);
GEC_DEF_GLOBAL(CARD_ONE_R, Bigint256, // 2^256 mod CARD
0x14551231950b75fc4402da1732fc9bebf_int);
// define the scalar type
using Scalar = GEC_BASE_FIELD(Bigint256, CARD, CARD_P, CARD_RR, CARD_ONE_R);
// parameters of the elliptic curve, in montgomery form
const Field A(0); // = A * 2^256 mod MOD
const Field B(0x700001ab7_int); // = B * 2^256 mod MOD
// define the curve with Jacobian coordinate
using Secp256k1_ = GEC_CURVE(gec::curve::JacobianCurve, Field, A, B);
// use the specialized implementation for curves whose A = 0 to boost performance
using Secp256k1 = GEC_CURVE_B(gec::curve::JacobianCurve, Field, B);
// define the generator, in montgomery form
const Secp256k1 GEN(
Field(0x9981e643e9089f48979f48c033fd129c231e295329bc66dbd7362e5a487e2097_int),
Field(0xcf3f851fd4a582d670b6b59aac19c1368dfc5d5d1f1dc64db15ea6d2d3dbabe2_int),
Field(0x1000003d1_int)
);
int main()
{
Secp256k1 p1, p2, p3;
Secp256k1::mul(p1, 1, GEN);
Secp256k1::mul(p2, 2, GEN);
Secp256k1::add(p3, p1, p2);
std::cout << p3;
return 0;
}
When p1 = GEN and p2 = GEN, we get p3 as follows
Secp256k1 p1, p2, p3;
Secp256k1::mul(p1, 1, GEN);
Secp256k1::mul(p2, 1, GEN);
Secp256k1::add(p3, p1, p2);
std::cout << p3;
{0x7c75dd9524177d59 3c03889b8dcd9b1c b05fb7d2a3da7fe8 ba9f29b104e7db13,
0x55debb381f4ad034 cc27cb48a46449aa a87d43fdb563384b 1cd20838e6fddc9f,
0x9e7f0a3fa94b05ac e16d6b355833826d 1bf8baba3e3b8c9b 62bd4da6a7b75b95}
When we just compute 2*GEN, we get a different value all together! This should not happen!
Secp256k1 p1;
Secp256k1::mul(p1, 2, GEN);
cout << p1;
{0x0000000000000000 0000000000000000 0000000000000000 0000000000000000,
0x0000000000000000 0000000000000000 0000000000000000 0000000000000000,
0x0000000000000000 0000000000000000 0000000000000000 0000000000000000}
Same thing for 3*GEN
Secp256k1 p1;
Secp256k1::mul(p1, 3, GEN);
cout << p1;
{0x0000000000000000 0000000000000000 0000000000000000 0000000000000000,
0x0000000000000000 0000000000000000 0000000000000000 0000000000000000,
0x0000000000000000 0000000000000000 0000000000000000 0000000000000000}
When p1 = 1GEN and p2 = 2GEN, we get different values
Secp256k1 p1, p2, p3;
Secp256k1::mul(p1, 1, GEN);
Secp256k1::mul(p2, 2, GEN);
Secp256k1::add(p3, p1, p2);
cout << p3;
{0x9981e643e9089f48 979f48c033fd129c 231e295329bc66db d7362e5a487e2097,
0xcf3f851fd4a582d6 70b6b59aac19c136 8dfc5d5d1f1dc64d b15ea6d2d3dbabe2,
0x0000000000000000 0000000000000000 0000000000000000 00000001000003d1}
When p1 = 4GEN, p2 = 7GEN and when p1 = 5GEN, p2 = 6GEN we get different values
Secp256k1 p1, p2, p3;
Secp256k1::mul(p1, 4, GEN);
Secp256k1::mul(p2, 7, GEN);
Secp256k1::add(p3, p1, p2);
cout << p3;
{0x1fb5dd34380d1e8d df3cf8dcfacd3c43 5d532ba6446d8835 a4e1b7ee985a3ad2,
0xa6a243e38cd1de5c 8bd5ab69e2291ed0 2073df16b7be047a 58dcce5a9df4f0bd,
0x64d172d2254b1eb3 179345a5659e730e 814a734b032e6909 61fa5d4503c4cc2e}
Secp256k1 p1, p2, p3;
Secp256k1::mul(p1, 5, GEN);
Secp256k1::mul(p2, 6, GEN);
Secp256k1::add(p3, p1, p2);
cout << p3;
{0xd942c4c06e83a9f5 e120c1290faf9e8a 2ef18b316e0d17c5 e6e5daacca8fff58,
0xf0bfc458cf399c0c 11715c0681eb3d2d 2ad8303fdd3fa737 92cefd0dbf159625,
0xcc7ba9867d26b3fd 23553b5204634b6d 4c8e97ec206fa6ae 0dbc34de96615f6f}
They are supposed to be the same as checked when done in Weierstrass form of the curve.
When p1=4GEN and p2=6GEN, we get
Secp256k1 p1, p2, p3;
Secp256k1::mul(p1, 4, GEN);
Secp256k1::mul(p2, 6, GEN);
Secp256k1::add(p3, p1, p2);
cout << p3;
{0xde7053edb5e85ddf 98953ed4b0f65454 adae855e9701f282 4d4b2f91dc02ee5a,
0xe6eb4afe1dc5fd6f f3f9a41b4ceb756f 00dcf2d8ebd0c6aa 93ee5d50600bc498,
0x4bebd311ef1b73d3 271587b52ae56b5e 23fd07dec301cf5b f779ca1675b48117}
But when p1=6GEN and p2=4GEN, we get
Secp256k1 p1, p2, p3;
Secp256k1::mul(p1, 6, GEN);
Secp256k1::mul(p2, 4, GEN);
Secp256k1::add(p3, p1, p2);
cout << p3;
{0xde7053edb5e85ddf 98953ed4b0f65454 adae855e9701f282 4d4b2f91dc02ee5a,
0x1914b501e23a0290 0c065be4b3148a90 ff230d27142f3955 6c11a2ae9ff43797,
0xb4142cee10e48c2c d8ea784ad51a94a1 dc02f8213cfe30a4 088635e88a4b7b18}
Notice how the first lines are the same..
Question - Can anyone help me out with this confusion? Can anyone explain what is happening? Is there an error in the code? Some constant out of whack?
Know that there is nothing special about the functionality of these points. Think of them as adding numbers. 0 is supposed to be 0. 4+7 should be equal to 5+6.