Основная концепция Lightning Network проста. Для дальнейшего понимания представьте потоки транзакций сети Lightning как код javascript, ссылаясь на белую книгу.
Если я объясню все потоки транзакций в одном сообщении в блоге, его будет трудно читать. Итак, я делю на несколько сообщений в блоге. Цель этого сообщения в блоге - потратить отзывную доставку, что соответствует рисунку 5 официального документа.
Код на Github: lightning-network-tx-flow
Следующее сообщение: Упрощенный код Bitcoin Lightning Network, часть 2 - Средство защиты от утечки
Упрощенные структуры блоков и транзакций
Настоящий биткойн сложен, поэтому я максимально упростил его. Определенные структуры блока и транзакции следующие.
class Block { constructor(hashPrev, txs) { this.hashPrev = hashPrev; this.txs = txs; } } class Transaction { constructor(txIns, txOuts) { this.txIns = txIns; this.txOuts = txOuts; } } class TxIn { constructor(txPrev, index, scriptSig) { this.txPrev = txPrev; this.index = index; this.scriptSig = scriptSig; } } class TxOut { constructor(value, scriptPubKey) { this.value = value; this.scriptPubKey = scriptPubKey; } }
Блок содержит только предыдущие хеши и транзакции. Точно так же транзакция содержит только входы и выходы.
6 шагов, чтобы провести отзывную доставку
Я разделяю весь процесс проведения Отзывной доставки на 6 этапов, как показано ниже. Обратите внимание, что на изображении выше написано C1a или C1b или так далее.
- Финансирование с несколькими подписями
- Сборка C1a и C1b (без знака)
- Сборка RD1a и RD1b
- Обмен подписью C1a и C1b
- Потратьте C1b
- Потратьте D1b
- Израсходовать RD1b
1. Финансирование с несколькими подписями
Сначала Алиса и Боб создают мультиподпись 2to2. Это не совсем точно, просто упрощенный вариант. Пожалуйста, обратитесь к моему предыдущему сообщению в блоге для дальнейшего понимания.
const redeemScript = { n: 2, m: 2, pubKeyHashs: [ getPubKeyHash(AliceKeys[1]), // Alice pubKeyHash getPubKeyHash(BobKeys[1]) // Bob pubKeyHash ] } const redeemScriptHash = CryptoJS.RIPEMD160(CryptoJS.SHA256( JSON.stringify(redeemScript) )).toString();
Алиса финансирует биткойн на этот «redeemScriptHash» (это как адрес).
Алиса и Боб уже депонировали 0,5 биткойна соответственно в качестве генезисного блока. Итак, Алиса потратит эти 0,5 биткойна на финансирование mulisig.
// Create blockchain let Blocks = [genesis]; const gTxH = calculateTxHash(Blocks[0].txs[0]); // Create unsingned transaction let fundingTxAlice = new Transaction( [ new TxIn( gTxH, 0, { type: 'SINGLE', sig: getPubKeyHash(AliceKeys[0]) // Place pubKeyHash for signing instead. } ) ], [ new TxOut( 50000000, { type: 'NORMAL', pubKeyHash: redeemScriptHash } ) ] ); // Sign transaction by Alice wallet fundingTxAlice = signTx(fundingTxAlice, AliceKeys[0]); // Create new Block as adding transactions const newBlock = createNewBlock( [fundingTxAlice, fundingTxBob], Blocks ) // Mine block Blocks = mineBlock(Blocks, newBlock);
Я не могу запустить код операции Биткойна, поэтому я определяю scriptSig и scriptPubKey как формат json.
scriptSig = { type: 'SINGLE', sig: { signature: signature, pubKey: pubKey } } scriptPubKey = { type: 'NORMAL', pubKeyHash: pubKeyHash }
Таким же образом Боб финансирует мультисиг, так что у мультисиг теперь 1 биткойн.
2. Сборка C1a и C1b (без признаков)
Используя 2 транзакции, которые ранее основали Алиса и Боб, Алиса создала C1a. Обратите внимание, что первый выход типа ведьмы C1a - «RSMS». Точное название - Отзывный договор о сроке погашения последовательности. Для этого требуется 2 подписи. После этого будет создана отзывная доставка. Другой выход - обычный.
В настоящий момент Боб не подписывает. Если Боб подпишет, Алиса может немедленно потратить эту транзакцию. В случае, если Алиса является злоумышленником и Алиса не подписывает RD1a, Боб навсегда теряет 0,5 биткойна, так что Боб не должен подписывать.
const fTxAH = calculateTxHash(fundingTxAlice); const fTxBH = calculateTxHash(fundingTxBob); let C1a = new Transaction( [ new TxIn( fTxAH, 0, { type: 'MULTI', sig: [ redeemScript.pubKeyHashs[0], redeemScript.pubKeyHashs[1] ], redeemScript } ), new TxIn( fTxBH, 0, { type: 'MULTI', sig: [ redeemScript.pubKeyHashs[0], redeemScript.pubKeyHashs[1] ], redeemScript } ) ], [ new TxOut( 50000000, { type: 'RSMS', pubKeyHash: [ getPubKeyHash(AliceKeys[2]), getPubKeyHash(BobKeys[2]) ] } ), new TxOut( 50000000, { type: 'NORMAL', pubKeyHash: getPubKeyHash(BobKeys[2]) } ) ] )
Json formate scriptSig и scriptPubKey следующие. Обратите внимание, что я включаю redeemScript среди scriptSig, чтобы потратить фонд с несколькими подписями.
scriptSig = { type: 'MULTI', sig: [ { signature: signature, pubKey: pubKey1 }, { signature: signature, pubKey: pubKey2 } ], redeemScript: redeemScript } scriptPubKey = { type: 'RSMS', pubKeyHash: [ pubKeyHash1, pubKeyHash2, ] }
Таким же образом Боб строит C1b.
3. Сборка RD1a и RD1b
RD1a имеет временную блокировку. Если Алиса потратит C1a, Алиса не сможет получить 0,5 биткойна до окончания периода временной блокировки. Я указываю временную блокировку как 3, так что Алисе нужно ждать подтверждения 3 блока. С другой стороны, Боб может получить 0,5 биткойна сразу, потому что у D1a нет временной привязки.
Кстати, в реальном случае временная блокировка может быть 1000.
Обратите внимание, что предыдущая транзакция в TxIn пуста. Потому что C1a еще не потратил, поэтому хеш транзакции посчитать невозможно. Для этого команда разработчиков Lightning предлагает новый код операции SIGHASH_NOINPUT
.
И обратите внимание, что RD1a подписан Бобом. Алисе необходимо передать RD1a Бобу и позволить ему подписать, чтобы RD1a потребовала подписи Боба для траты.
let RD1a = new Transaction( [ new TxIn( '', // Keep empty because C1a have not yet spent 0, { type: 'RD', sig: [ getPubKeyHash(AliceKeys[2]), getPubKeyHash(BobKeys[2]) ], sequence: 3 } ), ], [ new TxOut( 50000000, { type: 'NORMAL', pubKeyHash: getPubKeyHash(AliceKeys[3]) } ) ] ); // Hand over RD1a to Bob and let him sign RD1a = signTx(RD1a, BobKeys[2]);
Json formate RD scriptSig выглядит следующим образом. Это содержит временную блокировку.
scriptSig = { type: 'RD', sig: [ { signature: signature, pubKey: pubKey1 }, { signature: signature, pubKey: pubKey2 } ], sequence: 3 }
Таким же образом Боб строит RD1b.
4. Обмен подписью C1a и C1b
Алиса получает подпись Боба RD1a, так что пришло время обменяться подписью C1a. Алиса передает C1a Бобу и позволяет ему подписать. Теперь Алиса может потратить C1a в любое время.
C1a = signTx(C1a, BobKeys[1]);
Таким же образом Боб позволил Алисе подписать C1b.
5. Потратьте C1b
Давайте рассмотрим случай, когда Боб тратит C1b. Боб сам подписывает C1b. После проверки Боб передает C1b и добавляет в блокчейн.
// Sign by himself(Bob) C1b = signTx(C1b, BobKeys[1]); // Validate transaction validateTx(C1b, Blocks); // Mine block as adding transactions Blocks = mineBlock(Blocks, createNewBlock([C1b], Blocks));
6. Потратьте D1b.
Алиса может потратить D1b без временной блокировки, потому что биткойны, финансируемые с помощью мультиподписи, были потрачены Бобом как транзакция C1b. Если Боб злонамерен и не сотрудничает с Алисой, Боб может потратить C1b без согласия Алисы. В этом случае Боб несет временную блокировку.
const hashC1b = calculateTxHash(C1b); let D1b = new Transaction( [ new TxIn( hashC1b, 0, { type: 'SINGLE', sig: [ getPubKeyHash(AliceKeys[2]) ], } ) ], [ new TxOut( 50000000, { type: 'NORMAL', pubKeyHash: getPubKeyHash(AliceKeys[3]) } ) ] ) D1b = signTx(D1b, AliceKeys[2]); validateTx(D1b, Blocks); Blocks = mineBlock(Blocks, createNewBlock([D1b], Blocks));
7. Израсходовать RD1b
Сначала Боб пытается сразу потратить RD1b. Но результат - неудача из-за временной блокировки.
// Set previous transaction hash to txIn RD1b.txIns[0].txPrev = calculateTxHash(C1b); RD1b = signTx(RD1b, BobKeys[2]); validateTx(RD1b, Blocks); // => Fail, because of block time lock
После добавления 2 блоков Боб может успешно потратить RD1b, потому что время блокировки истекло.
// Set previous transaction hash to txIn RD1b.txIns[0].txPrev = calculateTxHash(C1b); RD1b = signTx(RD1b, BobKeys[2]); // Add 2 blocks for time lock Blocks = mineBlock(Blocks, createNewBlock([], Blocks)); Blocks = mineBlock(Blocks, createNewBlock([], Blocks)); validateTx(RD1b, Blocks); // => Success, because time lock expire Blocks = mineBlock(Blocks, createNewBlock([RD1b], Blocks));
Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик