Author

Topic: Валидация транзакций в TapRoot (Read 102 times)

sr. member
Activity: 770
Merit: 305
Ну, тащемта, я справился с первой половиной задачи.
Чуть-чуть привел в порядок мозги по поводу тапрута - что там есть простые транзакции где только подпись, и есть сложные с контрольным блоком, и деревом возможных redeem-скриптов. Вообще красиво, конечно. Самый простой пример: мы делали раньше мультисиг: допустим, с какого-то адреса могут забрать баблоса либо Алиса, либо Боб. Это multisig 1-из-2 в каком бы обличии он ни был - в bare-multisig, потом в p2sh, потом в p2wsh. В любом случае в блокчейн записывали оба публичных ключа. А нахуа? Теперь если выводит бабло Боб - он свою подпись ставит и свой скрипт. И никто никогда не узнает что к этой транзакции имела отношение Алиса. Ну да это детали.

Я свою реализацию schnorr-подписей сделал для подписывания транзакций. Стыдно признаться, но ихнюю библиотеку secp256k1 которую они повсеместно используют в биткойне я не могу в свой виндусовый проект вставить. У меня не получается. Пришлось переписать на использование встроенного в Qt OpenSSL. По скорости оно хуже, но мне торопиться некуда. Мне важнее чтобы за попытками оптимизации можно было разглядеть алгоритм. Выглядит примерно так. Ну, это фрагмент, конечно.

Quote
const MyKey32 Schnorr::tagged_hash ( const char* tag, const QByteArray& data ) const
{
  const MyKey32 tag_hash ( MyKey32::brain ( tag ) );
  return MyByteArray ( tag_hash ).putArray ( tag_hash ).putArray ( data ).sha256 ( );
}

const QByteArray Schnorr::sign ( const MyKey32& msg, const MyKey32& priv, const MyKey32& aux ) const
{
  const MyEcPoint p ( ctx, MyBigNum ( priv ) );
  const MyKey32 x ( p.x ( ) );
  const MyKey32 y ( p.y ( ) );
  const MyKey32 d ( ( y.constData ( )[31] & 1 ) ? MyKey32::SUB ( n, priv ) : priv );
  const MyKey32 t ( MyKey32::XOR ( d, tagged_hash ( "BIP0340/aux", aux ) ) );
  const MyKey32 k0 ( tagged_hash ( "BIP0340/nonce", QByteArray ( t ).append ( x ).append ( msg ) ) );
  const MyEcPoint r ( ctx, MyBigNum ( k0 ) );
  const MyKey32 R ( r.x ( ) );
  const MyKey32 e ( tagged_hash ( "BIP0340/challenge", QByteArray ( R ).append ( x ).append ( msg ) ) );
  const MyKey32 k ( ( r.y ( ).constData ( )[31] & 1 ) ? MyKey32::SUB ( n, k0 ) : k0 );
  return MyByteArray ( R ).putArray ( MyKey32::ADD ( k, MyKey32::MUL ( e, d ) ) );
}
sr. member
Activity: 770
Merit: 305
Здесь у вас есть координата x точки P, принадлежащей кривой.
Я очень благодарен Вам, но ещё испытываю некоторое неудобство и даже, можно сказать стыд.
Вы не поленились поискать баг в моём коде, а я занялся некоторыми другими исследованиями, пока не смог эту задачку понять для себя.
Выходит, я у Вас отнял время впустую Sad
Вы старались, морщили ум... А я все равно пока не могу Вашей помощью воспользоваться, потому как "не сделал домашнюю работу".
Пусть отлежится в голове, я с такой скоростью воспринимать эти вещи не могу.
Всё-таки, посложнее бинома Ньютона.
Откуда Вы-то это узнали? В школе же этому не учили!

Я еще понял, что сами тапруты бывают (как минимум) двух видов. На этапе когда мы видим адрес в блокчейне - мы не знаем, там (1) сложный скрипт с меркль-хэшами и вычислениями, инскрипшенами или (2) простой вариант перевода с адреса на адрес, то есть в витнесе только один элемент длиной 64 байта. Пока займусь самыми простыми вещами, а там посмотрим.
legendary
Activity: 2317
Merit: 2318
Code:
# 4. Set P by interpreting p as the x coordinate for a point on secp256k1 where the y coordinate is even  
    P = point_mul(G,p)                                                     
    assert has_even_y(P)
Здесь у вас есть координата x точки P, принадлежащей кривой. Чтобы получить полноценную точку P, необходимо из её координаты x вычислить координату y. И делается это не перемножением x на генератор G, а по-другому, при помощи функции lift_x().
sr. member
Activity: 770
Merit: 305
Кто-нибудь понимает как клиент определяет валидность TapRoot-транзакции?
Я неделю ломаю голову над кодом из десяти строчек. У меня не сходится. И как понять что я делаю не так?
Не, ну есть способ, который 100% приведет к результату:
1) Поставить новую убунту (у меня старая, а код Bitcoin-Core хочет чтобы компилятор был бы С++2017 ) на виртуальной машине
2) Скомпилять там сорцы клиента, вставив бы туда в нужные места логгирование всего на свете
3) Отправить себе (в тестовой сети) транзакцию на p2tr-адрес
4) Запустить бы в том клиенте в консоли команду testmempoolaccept заведомо валидной транзакции вывода с этого адреса

Но, Карл! Карл! Ради 10 строчек? Может уже кто-то занимался этим?

Я уже и с питоном подружился, и умножение на кривых стал чуть больше понимать
Мне тут говорят, мол все просто: https://bitcointalksearch.org/topic/m.64332617
Хуяк-хуяк и все получается типа. А у меня не получается. Па-ма-ги-тя!

Взялся проверить валидность транзакции
https://www.blockchain.com/explorer/transactions/btc/445f592c71e364670ecf8d168eb651319d1fc17527df7addd35aa09555733cd6

Код вот такой (извините за хардкод констант, это мой первый опыт в питоне):
Code:
def my_test() -> bool:
    key          = 0x2913b252fe537830f843bfdc5fa7d20ba48639a87c86ff837b92d083c55ad7c1
    cblock       = "c00000000000000000000000000000000000000000000000000000000000000001"
    leaf_version = 0 # ( 0xc0 & 0xfe ) ### hardcoded
# 3. Extract p as the 2nd to 33rd bytes of the control block.
    p = 0x0000000000000000000000000000000000000000000000000000000000000001;
# 4. Set P by interpreting p as the x coordinate for a point on secp256k1 where the y coordinate is even 
    P = point_mul(G,p)                                                     
    assert has_even_y(P)
#4. Compute the hash k of the script as follows: sha256(sha256("TapLeaf") || sha256("TapLeaf") || leaf_version || compact_size(script) || script), where || is concatenation
    data0 = leaf_version.to_bytes ( 1 ) + bytes.fromhex ( "0151" )
    k = int_from_bytes ( tagged_hash ( "TapLeaf", data0 ) )
#5. Compute t as sha256(sha256("TapTweak") || sha256("TapTweak") || p || k) and interpret it as an integer.
    data1 = bytes_from_int ( p ) + bytes_from_int ( k )
    t = int_from_bytes ( tagged_hash ( "TapTweak", data1 ) )
#6. Let Q = P + t * G where G is the generator for secp256k1
    Q = point_add ( P, point_mul ( G, t ) )
#7. Check the x coordinate of Q matches the key in the output script
    xx = x ( Q ) 
    debug_print_vars()
    return True

Я эту функцию засунул в https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.py чтобы не писать всю мумбу-юмбу
По идее, в конце xx должно хоть как-то коррелировать с key
А у меня в результатах совершенно другое. И в какой из десяти строчек ошибка?
Code:
  Variables in function my_test at line 140:
           key == 0x2913b252fe537830f843bfdc5fa7d20ba48639a87c86ff837b92d083c55ad7c1
        cblock == c00000000000000000000000000000000000000000000000000000000000000001
   leaf_version == 0x0000000000000000000000000000000000000000000000000000000000000000
             p == 0x0000000000000000000000000000000000000000000000000000000000000001
             P == ('0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', '0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
         data0 == 0x000151
             k == 0x8fe98416aeedc4ff4bdd55d79dd3e5399c9c2b1d2d2beea8c300c2ba262c5bbc
         data1 == 0x00000000000000000000000000000000000000000000000000000000000000018fe98416aeedc4ff4bdd55d79dd3e5399c9c2b1d2d2beea8c300c2ba262c5bbc
             t == 0xf1643f7a852c98c9319797a4e0177999288bfd5c788fad852f5973357eb38d5e
             Q == ('0xc090e69051099c46d8f33dace5549623e5afc52a51cc545d6bd077ccc33098b2', '0xb289f42f0098f658717a44679b2f0571eb9ea0034d78e8e9639cbe48133f3cc9')
            xx == 0xc090e69051099c46d8f33dace5549623e5afc52a51cc545d6bd077ccc33098b2

Jump to: