Last active
March 30, 2024 01:03
-
-
Save jspilman/5424310 to your computer and use it in GitHub Desktop.
Proof of Concept for Payment Channels
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 0) User and AP negotiate how much to escrow, who pays the fees, and how far in the future nLockTime | |
// will be set (how long user’s funds will be tied if AP doesn’t close the channel) | |
long apUnspent = Util.Amount("0.27"); | |
long userUnspent = Util.Amount("6.123"); | |
long escrowAmount = Util.Amount("5"); | |
long payerFee = 0; | |
long apFee = Util.Amount(".0005"); | |
long userChange = userUnspent - escrowAmount - payerFee; | |
long apChange = apUnspent - apFee; | |
uint lockTime = Util.LockTime(DateTime.Now.AddMinutes(15)); | |
// BIP32 -- User/0' | |
byte[] entropy = Util.DoubleSHA(Encoding.ASCII.GetBytes("Payment Channel User")); | |
HdNode userWallet = HdNode.Create(entropy, mainNet: false).GetSecretChild(0); | |
// BIP32 -- AP/0' | |
entropy = Util.DoubleSHA(Encoding.ASCII.GetBytes("Payment Channel AP")); | |
HdNode apWallet = HdNode.Create(entropy, mainNet: false).GetSecretChild(0); | |
// The TxID on Test-Net funding some unspent balances into our HD wallets | |
byte[] unspentTxId = Util.HexToBytes("96f9ac822c0259b8e55067141c3a438f4ea65492244149aea306a4197b638468"); | |
Array.Reverse(unspentTxId); | |
// Prep the User's unspent | |
Script userUnspentScriptPubKey = TxOut.Addr(userUnspent, userWallet.GetPublicChild(0, 0).PublicKey).ScriptPubKey; | |
TxIn userUnspentTxIn = new TxIn(unspentTxId, 2); // Output '2' of 'unspentTxId' is the users | |
// Prep a AP's unspent | |
// Spend from AP/0'/0/0 | |
Script apUnspentScriptPubKey = TxOut.Addr(apUnspent, apWallet.GetPublicChild(0, 0).PublicKey).ScriptPubKey; | |
TxIn apUnspentTxIn = new TxIn(unspentTxId, 0); // Output '0' of 'unspentTxId' is the users | |
// 1) User creates an unsigned TX1 with 1 or more inputs from user’s ‘listunspent’, | |
// change going back to user (if any), and a single output of ‘FundAmount’ | |
// with scriptPubKey of ‘2 PK1 OP_0 2 CHECKMULTISIG’, and sends to the AP | |
Transaction tx1 = new Transaction(); | |
tx1.Vin.Add(userUnspentTxIn); | |
// MULTISIG is ‘2 PK1 OP_0 2 CHECKMULTISIG’ | |
// Setup PK1 from User/0'/0/1 | |
tx1.Vout.Add(TxOut.MultiSig(escrowAmount, 2, 2, userWallet.GetPublicChild(0, 1).PublicKey)); | |
// Output change back to User/0'/1/0 | |
tx1.Vout.Add(TxOut.Addr(userChange, userWallet.GetPublicChild(1, 0).PublicKey)); | |
Console.WriteLine("Step 1) Unsigned Funding Transaction:"); | |
Console.WriteLine(" Raw: " + Util.BytesToHex(tx1.Serialize())); | |
Console.WriteLine(" TxID: " + Util.BytesToHexAsBigEndian(tx1.TxID)); | |
// 2) AP adds to TX1; their inputs (if any), their change (if any), replaces OP_0 in the scriptPubKey | |
// with a PK they control, signs SIGHASH_ALL, and returns TX1 to User. | |
tx1.Vin.Add(apUnspentTxIn); | |
// Change back to AP/0'/1/0 | |
tx1.Vout.Add(TxOut.Addr(apChange, apWallet.GetPublicChild(1, 0).PublicKey)); | |
// Replace OP_0 in slot 2 with PK2 from AP/0'/0/1 | |
tx1.Vout[0].ScriptPubKey.SetOp(2, apWallet.GetPublicChild(0, 1).PublicKey); | |
// AP signs Input 1 with AP/0'/0/0, and push the corresponding public key | |
tx1.Sign(apWallet.GetPublicChild(0, 0).PrivateKey, apUnspentScriptPubKey, 1, SigHash.All); | |
tx1.Vin[1].ScriptSig.Push(apWallet.GetPublicChild(0, 0).PublicKey); | |
Console.WriteLine("Step 2) AP Signed Funding Transaction: "); | |
Console.WriteLine(" Raw: " + Util.BytesToHex(tx1.Serialize())); | |
Console.WriteLine(" TxID: " + Util.BytesToHexAsBigEndian(tx1.TxID)); | |
// 3) User verifies TX1 is as agreed, and signs it SIGHASH_ALL, but keeps it to himself. | |
// User, having completed TX1, knows its TxID and can now create TX2-Locked spending TX1 back to themselves. | |
// User sets nLockTime to the agreed point, signs SIGHASH_ALL, and sends TX2-Locked to AP. | |
// User signs Input 0 with User/0'/0/0, and push the corresponding public key | |
tx1.Sign(userWallet.GetPublicChild(0, 0).PrivateKey, userUnspentScriptPubKey, 0, SigHash.All); // User Signs Input 0 | |
tx1.Vin[0].ScriptSig.Push(userWallet.GetPublicChild(0, 0).PublicKey); | |
Console.WriteLine("Step 3a) Fully Signed Funding Transaction: "); | |
Console.WriteLine(" Raw: " + Util.BytesToHex(tx1.Serialize())); | |
Console.WriteLine(" TxID: " + Util.BytesToHexAsBigEndian(tx1.TxID)); | |
// Verify TX1 is complete given the scriptPubKeys from the input transactions | |
Assert.IsTrue(tx1.Verify(userUnspentScriptPubKey, apUnspentScriptPubKey)); | |
// Create the Refund TX2 with SEQ = 0 and LockTime set, spending TX1 | |
// Amount spend will be full amount minus a fee | |
// Send output to User/0'/0/2 | |
Transaction tx2Locked = new Transaction(); | |
tx2Locked.Vin.Add(new TxIn(tx1.TxID, 0)); | |
tx2Locked.Vin[0].Sequence = 0; | |
tx2Locked.LockTime = lockTime; | |
tx2Locked.Vout.Add(TxOut.Addr(escrowAmount - Util.Amount(".0005"), userWallet.GetPublicChild(0, 2).PublicKey)); | |
// Workaround historic Bitcoin bug... | |
tx2Locked.Vin[0].ScriptSig = Script.Parse(new byte[] { (byte)Script.OpCode.OP_0 }); | |
// Add a signature to MULTISIG Input with User/0'/0/1 | |
tx2Locked.Sign(userWallet.GetPublicChild(0, 1).PrivateKey, tx1.Vout[0].ScriptPubKey, 0, SigHash.All); | |
Console.WriteLine("Step 3b) User Signed TX2-Locked Refund Transaction: "); | |
Console.WriteLine(" Raw: " + Util.BytesToHex(tx2Locked.Serialize())); | |
Console.WriteLine(" TxID: " + Util.BytesToHexAsBigEndian(tx2Locked.TxID)); | |
// 4) AP verifies TX2-Locked and adds their signature, and returns TX2-Locked to User. | |
// User can now broadcast TX1 and TX2-Locked. | |
// Add a signature to MULTISIG Input with AP/0'/0/1 | |
tx2Locked.Sign(apWallet.GetPublicChild(0, 1).PrivateKey, tx1.Vout[0].ScriptPubKey, 0, SigHash.All); | |
Console.WriteLine("Step 4) AP Signed TX2-Locked Refund Transaction: "); | |
Console.WriteLine(" Raw: " + Util.BytesToHex(tx2Locked.Serialize())); | |
Console.WriteLine(" TxID: " + Util.BytesToHexAsBigEndian(tx2Locked.TxID)); | |
// Verify Refund TX2 is complete given the input's scriptPubKey (MULTISIG) | |
Assert.IsTrue(tx2Locked.Verify(tx1.Vout[0].ScriptPubKey)); | |
// 5) At each payment milestone, user creates TX2-Final; TX1 as input, final nSequence, no lock time, | |
// with a single output going back to to the user, and an amount equal to the remaining balance | |
// of the channel. User signs with SIGHASH_SINGLE and sends to the AP. | |
// TX2 with 3BTC back to the User/0'/0/2 | |
Transaction tx2Final = new Transaction(); | |
tx2Final.Vin.Add(new TxIn(tx1.TxID, 0)); | |
tx2Final.Vout.Add(TxOut.Addr(Util.Amount("3"), userWallet.GetPublicChild(0, 2).PublicKey)); | |
// Workaround historic Bitcoin bug... | |
tx2Final.Vin[0].ScriptSig = Script.Parse(new byte[] { (byte)Script.OpCode.OP_0 }); | |
// Add a signature to MULTISIG Input with User/0'/0/1, SIGHASH_SINGLE | |
tx2Final.Sign(userWallet.GetPublicChild(0, 1).PrivateKey, tx1.Vout[0].ScriptPubKey, 0, SigHash.Single); | |
Console.WriteLine("Step 5) Partial 2BTC Release from User: "); | |
Console.WriteLine(" Raw: " + Util.BytesToHex(tx2Final.Serialize())); | |
Console.WriteLine(" TxID: " + Util.BytesToHexAsBigEndian(tx2Final.TxID)); | |
// 6) AP can add an output to TX2-Final sending their portion of the coins where ever they want, | |
// sign it SIGHASH_ALL, and broadcast it at any point before nLockTime, closing the channel. | |
// Pay the balance to AP/0'/0/2, minus 0.0005 fee | |
tx2Final.Vout.Add(TxOut.Addr(Util.Amount("1.9995"), apWallet.GetPublicChild(0, 2).PublicKey)); | |
// Add a signature to MULTISIG Input with AP/0'/0/1, SIGHASH_ALL | |
tx2Final.Sign(apWallet.GetPublicChild(0, 1).PrivateKey, tx1.Vout[0].ScriptPubKey, 0, SigHash.All); | |
Console.WriteLine("Step 6) AP Closes Payment Channel: "); | |
Console.WriteLine(" Raw: " + Util.BytesToHex(tx2Final.Serialize())); | |
Console.WriteLine(" TxID: " + Util.BytesToHexAsBigEndian(tx2Final.TxID)); | |
Assert.IsTrue(tx2Final.Verify(tx1.Vout[0].ScriptPubKey)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Step 1) Unsigned Funding Transaction: | |
Raw: 01000000016884637B19A406A3AE4941249254A64E8F433A1C146750E5B859022C82ACF9960200000000FFFFFFFF020065CD1D00000000265221035F3CC5A102F06F4FFBD8364DE0E224BDCA0C72263252BC1748742223624492820052AEE08FB106000000001976A9148C6DF2178FABB7467C81A741EAEAB4ACEF0B0E2388AC00000000 | |
TxID: B8345F01EF074F4A6BA8B16E81FA5BEDA5617A356F88CAECAF9403EA9B28F018 | |
Step 2) AP Signed Funding Transaction: | |
Raw: 01000000026884637B19A406A3AE4941249254A64E8F433A1C146750E5B859022C82ACF9960200000000FFFFFFFF6884637B19A406A3AE4941249254A64E8F433A1C146750E5B859022C82ACF996000000006B483045022100D3A13758BA268A9CB22409B70EBFF855CFE1B17F05B767BE0004AEFBC9E53DCF0220685C5A88A1795C6C977D7699DD91697114FD6B36FFD3A81E08D2D95D751BE2EB012103DF6BC1C448E327881C039EA151FE1BC356C8DE9E0698CB6B303C922DBBD975C9FFFFFFFF030065CD1D00000000475221035F3CC5A102F06F4FFBD8364DE0E224BDCA0C72263252BC1748742223624492822102ED8994D8920B72B924D758DAF85DBE4F7032B44FE119708143E31BA7813D2F7A52AEE08FB106000000001976A9148C6DF2178FABB7467C81A741EAEAB4ACEF0B0E2388AC70399B01000000001976A914849C3894294EF13A9FF7017FAB9F24A20AB51AE588AC00000000 | |
TxID: 030F4EBAC4D977DEFFC65AA1D92D8C3543FE6C54565A6864E206A65232F0C7A9 | |
Step 3a) Fully Signed Funding Transaction: | |
Raw: 01000000026884637B19A406A3AE4941249254A64E8F433A1C146750E5B859022C82ACF996020000006A473044022038D71C5BE10340A1C2185FBCD4519E1100634903814C09068E0AFA0D79DE8EC002206C00CC767B5227DFE83E28051624A6934223015D3B6AA8BEA68FA0B4E4CC45C20121025DD355F911396ECB07543FD4DA617D9E7C9752E1EC482139E43B5B47D8809614FFFFFFFF6884637B19A406A3AE4941249254A64E8F433A1C146750E5B859022C82ACF996000000006B483045022100D3A13758BA268A9CB22409B70EBFF855CFE1B17F05B767BE0004AEFBC9E53DCF0220685C5A88A1795C6C977D7699DD91697114FD6B36FFD3A81E08D2D95D751BE2EB012103DF6BC1C448E327881C039EA151FE1BC356C8DE9E0698CB6B303C922DBBD975C9FFFFFFFF030065CD1D00000000475221035F3CC5A102F06F4FFBD8364DE0E224BDCA0C72263252BC1748742223624492822102ED8994D8920B72B924D758DAF85DBE4F7032B44FE119708143E31BA7813D2F7A52AEE08FB106000000001976A9148C6DF2178FABB7467C81A741EAEAB4ACEF0B0E2388AC70399B01000000001976A914849C3894294EF13A9FF7017FAB9F24A20AB51AE588AC00000000 | |
TxID: C4358D6D2A663878DBD0DAADC833D81F3165A8B27F079521695CDDF1F5F3AF5D | |
Step 3b) User Signed TX2-Locked Refund Transaction: | |
Raw: 01000000015DAFF3F5F1DD5C692195077FB2A865311FD833C8ADDAD0DB7838662A6D8D35C4000000004A004830450220286CDB2145C03832798AF23060CF4BD22210ADA781B4C502A7518E7F89F64963022100F593EC8678DE3AD1D151DDD1CB41FF16D14E5480B096F9E2EF1A636909E29B3C010000000001B0A1CC1D000000001976A9146B36A2619F64221C2D24C5BCA32301728EA5439288AC63067751 | |
TxID: 24D60000CE8EE112B321DC68ADF57B1EFCD1C5AEE5932EFBB083B1F3CE299A0A | |
Step 4) AP Signed TX2-Locked Refund Transaction: | |
Raw: 01000000015DAFF3F5F1DD5C692195077FB2A865311FD833C8ADDAD0DB7838662A6D8D35C40000000092004830450220286CDB2145C03832798AF23060CF4BD22210ADA781B4C502A7518E7F89F64963022100F593EC8678DE3AD1D151DDD1CB41FF16D14E5480B096F9E2EF1A636909E29B3C0147304402204343DF2F601E6DFB85D3784B980ABAB243B5E94BF4035D48D2ABCCAC4E20D17A0220593D4F5AED4BB000943650112C69CA39B68286CFF0A83B9E2E102412FAE17BCC010000000001B0A1CC1D000000001976A9146B36A2619F64221C2D24C5BCA32301728EA5439288AC63067751 | |
TxID: 2B85DF59F75C044A17E4680F58F3F7449933C7279FB42314159AD4F35E603784 | |
Step 5) Partial 2BTC Release from User: | |
Raw: 01000000015DAFF3F5F1DD5C692195077FB2A865311FD833C8ADDAD0DB7838662A6D8D35C400000000490047304402204F833B0FB58C63AEF62BC30474332DBD4A4DB31BEBF2532DFADAD0F11535B830022039637C254AC29FA0CEE941F461CA7AF262128C2546EF6208FA66E739E911755803FFFFFFFF0100A3E111000000001976A9146B36A2619F64221C2D24C5BCA32301728EA5439288AC00000000 | |
TxID: 7CA4EF10FAB27584D89BF8DE4E17DD038C2094BAE1ACDD76002A0EF6E9B96EB0 | |
Step 6) AP Closes Payment Channel: | |
Raw: 01000000015DAFF3F5F1DD5C692195077FB2A865311FD833C8ADDAD0DB7838662A6D8D35C400000000910047304402204F833B0FB58C63AEF62BC30474332DBD4A4DB31BEBF2532DFADAD0F11535B830022039637C254AC29FA0CEE941F461CA7AF262128C2546EF6208FA66E739E91175580347304402207AC76A2EB31FE15242BC602395258B2D3CC9E36B6E6DA1819CB1F81794E242F6022034228D0EA3D84C20F29B9BC20455BB8EF666A925151008B7D449D5C277CC39B001FFFFFFFF0200A3E111000000001976A9146B36A2619F64221C2D24C5BCA32301728EA5439288ACB0FEEA0B000000001976A9142F8872AA0F6918FF45BC61272D9A68AC3EF0C54388AC00000000 | |
TxID: 2F6B75669CE595CA3BB13458A7E848F113A96F7761E10FD235C4CFBE605DBFCD |
Only …..voor ander persoon is als ging niet goed dag En nacht
For you is
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
โปรดอนุมัติเงินให้ฉันเพื่อรับเงินขอบคุณ