Skip to content

Instantly share code, notes, and snippets.

@jspilman
Last active March 30, 2024 01:03
Show Gist options
  • Save jspilman/5424310 to your computer and use it in GitHub Desktop.
Save jspilman/5424310 to your computer and use it in GitHub Desktop.
Proof of Concept for Payment Channels
// 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));
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
@Mariam1414
Copy link

โปรดอนุมัติเงินให้ฉันเพื่อรับเงินขอบคุณ

@sheymanabizada404
Copy link

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