static void Main(string[] args) { //Specify bitcoin network Network network = Network.TestNet; KeyService keyService = new KeyService(network); //Generates a brand new bitcoin address and private keys. MultiSigAddress address2 = keyService.GenerateNewAddress(2, 3); //Returns existing bitcoin address using existing private keys. MultiSigAddress address1 = keyService.GetExistingAddress(); //Create me some private keys for fun List <BitcoinSecret> keys = keyService.CreatePrivateKeys(3); /////////////////////////////////////////// //Ignore this stuff. Just testing out some ideas. //_bitcoinSession = new BitcoinSession(_network); //BitCoinTransactionService bcService = new BitCoinTransactionService(_bitCoinSession); //bcService.GetTransactionInfo("1f5dd12cfc57647bc2fe12832582fda1e46eb6cb1ea1b6cce35d1817ac06c691"); Console.ReadLine(); }
public void StoringDepositsWhenTargetIsMultisigIsIgnoredIffOurMultisig() { var dataFolder = new DataFolder(TestBase.CreateTestDir(this)); this.Init(dataFolder); this.AddFunding(); this.AppendBlocks(WithdrawalTransactionBuilder.MinConfirmations); MultiSigAddress multiSigAddress = this.wallet.MultiSigAddress; using (ICrossChainTransferStore crossChainTransferStore = this.CreateStore()) { crossChainTransferStore.Initialize(); crossChainTransferStore.Start(); TestBase.WaitLoopMessage(() => (this.ChainIndexer.Tip.Height == crossChainTransferStore.TipHashAndHeight.Height, $"ChainIndexer.Height:{this.ChainIndexer.Tip.Height} Store.TipHashHeight:{crossChainTransferStore.TipHashAndHeight.Height}")); Assert.Equal(this.ChainIndexer.Tip.HashBlock, crossChainTransferStore.TipHashAndHeight.HashBlock); // Forwarding money already in the multisig address to the multisig address is ignored. BitcoinAddress address1 = multiSigAddress.RedeemScript.Hash.GetAddress(this.network); BitcoinAddress address2 = new Script("").Hash.GetAddress(this.network); var deposit1 = new Deposit(0, new Money(160m, MoneyUnit.BTC), address1.ToString(), crossChainTransferStore.NextMatureDepositHeight, 1); var deposit2 = new Deposit(1, new Money(160m, MoneyUnit.BTC), address2.ToString(), crossChainTransferStore.NextMatureDepositHeight, 1); MaturedBlockDepositsModel[] blockDeposits = new[] { new MaturedBlockDepositsModel( new MaturedBlockInfoModel() { BlockHash = 1, BlockHeight = crossChainTransferStore.NextMatureDepositHeight }, new[] { deposit1, deposit2 }) }; crossChainTransferStore.RecordLatestMatureDepositsAsync(blockDeposits).GetAwaiter().GetResult(); Transaction[] partialTransactions = crossChainTransferStore.GetTransfersByStatus(new[] { CrossChainTransferStatus.Partial }).Select(x => x.PartialTransaction).ToArray(); Transaction[] suspendedTransactions = crossChainTransferStore.GetTransfersByStatus(new [] { CrossChainTransferStatus.Suspended }).Select(x => x.PartialTransaction).ToArray(); // Only the deposit going towards a different multisig address is accepted. The other is ignored. Assert.Single(partialTransactions); Assert.Empty(suspendedTransactions); IWithdrawal withdrawal = this.withdrawalExtractor.ExtractWithdrawalFromTransaction(partialTransactions[0], null, 1); Assert.Equal((uint256)1, withdrawal.DepositId); } }
internal static void EnsureDefaultMultisigAddress(string passphrase, X1WalletFile x1WalletFile) { //if (x1WalletFile.ColdStakingAddresses.Count > 0) // return; var decryptedSeed = VCL.DecryptWithPassphrase(passphrase, x1WalletFile.HdSeed); KeyMaterial myKeyMaterial = KeyHelper.CreateHdKeyMaterial(decryptedSeed, passphrase, C.Network.Consensus.CoinType, AddressType.MultiSig, C.External, 0); PubKey myPubKey = myKeyMaterial.GetKey(passphrase).PubKey.Compress(); // other Key KeyMaterial otherKeyMaterial = KeyHelper.CreateHdKeyMaterial(decryptedSeed, passphrase, C.Network.Consensus.CoinType, AddressType.MultiSig, C.External, 1); var otherPubKey = otherKeyMaterial.GetKey(passphrase).PubKey.Compress(); // The redeem script looks like: // 1 03fad6426522dbda5c5a9f8cab24a54ccc374517ad8790bf7e5a14308afc1bf77b 0340ecf2e20978075a49369e35269ecf0651d2f48061ebbf918f3eb1964051f65c 2 OP_CHECKMULTISIG Script redeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(1, myPubKey, otherPubKey); // The address looks like: // odx1qvar8r29r8llzj53q5utmcewpju59263h38250ws33lp2q45lmalqg5lmdd string bech32ScriptAddress = redeemScript.WitHash.GetAddress(C.Network).ToString(); // In P2SH payments, we refer to the hash of the Redeem Script as the scriptPubKey. It looks like: // 0 674671a8a33ffe295220a717bc65c19728556a3789d547ba118fc2a0569fdf7e Script scriptPubKey = redeemScript.WitHash.ScriptPubKey; var scp = scriptPubKey.ToString(); var multiSigAddress = new MultiSigAddress { OwnKey = myKeyMaterial, Address = bech32ScriptAddress, AddressType = AddressType.MultiSig, Label = "Default 1-of-2 MultiSig", MaxSignatures = 2, LastSeenHeight = null, SignaturesRequired = 1, RedeemScriptHex = redeemScript.ToBytes().ToHexString(), ScriptPubKeyHex = scriptPubKey.ToBytes().ToHexString(), OtherPublicKeys = new System.Collections.Generic.Dictionary <string, string>(), }; multiSigAddress.OtherPublicKeys.Add(otherPubKey.ToBytes().ToHexString(), "Bob"); x1WalletFile.MultiSigAddresses[multiSigAddress.Address] = multiSigAddress; }
public void StoringDepositsWhenWalletBalanceSufficientSucceedsWithDeterministicTransactions() { var dataFolder = new DataFolder(TestBase.CreateTestDir(this)); this.Init(dataFolder); this.AddFunding(); this.AppendBlocks(WithdrawalTransactionBuilder.MinConfirmations); MultiSigAddress multiSigAddress = this.wallet.MultiSigAddress; using (ICrossChainTransferStore crossChainTransferStore = this.CreateStore()) { crossChainTransferStore.Initialize(); crossChainTransferStore.Start(); TestBase.WaitLoopMessage(() => (this.ChainIndexer.Tip.Height == crossChainTransferStore.TipHashAndHeight.Height, $"ChainIndexer.Height:{this.ChainIndexer.Tip.Height} Store.TipHashHeight:{crossChainTransferStore.TipHashAndHeight.Height}")); Assert.Equal(this.ChainIndexer.Tip.HashBlock, crossChainTransferStore.TipHashAndHeight.HashBlock); BitcoinAddress address1 = (new Key()).PubKey.Hash.GetAddress(this.network); BitcoinAddress address2 = (new Key()).PubKey.Hash.GetAddress(this.network); var deposit1 = new Deposit(0, new Money(160m, MoneyUnit.BTC), address1.ToString(), crossChainTransferStore.NextMatureDepositHeight, 1); var deposit2 = new Deposit(1, new Money(60m, MoneyUnit.BTC), address2.ToString(), crossChainTransferStore.NextMatureDepositHeight, 1); MaturedBlockDepositsModel[] blockDeposits = new[] { new MaturedBlockDepositsModel( new MaturedBlockInfoModel() { BlockHash = 1, BlockHeight = crossChainTransferStore.NextMatureDepositHeight }, new[] { deposit1, deposit2 }) }; crossChainTransferStore.RecordLatestMatureDepositsAsync(blockDeposits).GetAwaiter().GetResult(); Transaction[] transactions = crossChainTransferStore.GetTransfersByStatus(new [] { CrossChainTransferStatus.Partial }).Select(x => x.PartialTransaction).ToArray(); Assert.Equal(2, transactions.Length); // Transactions[0] inputs. Ordered deterministically, roughly a mixture of time and canonical ordering. Assert.Equal(2, transactions[0].Inputs.Count); Assert.Equal(this.fundingTransactions[0].GetHash(), transactions[0].Inputs[0].PrevOut.Hash); Assert.Equal((uint)0, transactions[0].Inputs[0].PrevOut.N); Assert.Equal(this.fundingTransactions[0].GetHash(), transactions[0].Inputs[1].PrevOut.Hash); Assert.Equal((uint)1, transactions[0].Inputs[1].PrevOut.N); // Transaction[0] outputs. Assert.Equal(3, transactions[0].Outputs.Count); // Transaction[0] output value - change. Assert.Equal(new Money(10m, MoneyUnit.BTC), transactions[0].Outputs[0].Value); Assert.Equal(multiSigAddress.ScriptPubKey, transactions[0].Outputs[0].ScriptPubKey); // Transaction[0] output value - recipient 1. Assert.Equal(new Money(159.99m, MoneyUnit.BTC), transactions[0].Outputs[1].Value); Assert.Equal(address1.ScriptPubKey, transactions[0].Outputs[1].ScriptPubKey); // Transaction[0] output value - op_return. Assert.Equal(new Money(0m, MoneyUnit.BTC), transactions[0].Outputs[2].Value); new OpReturnDataReader(this.loggerFactory, this.federatedPegOptions).TryGetTransactionId(transactions[0], out string actualDepositId); Assert.Equal(deposit1.Id.ToString(), actualDepositId); // Transactions[1] inputs. Assert.Single(transactions[1].Inputs); Assert.Equal(this.fundingTransactions[1].GetHash(), transactions[1].Inputs[0].PrevOut.Hash); Assert.Equal((uint)0, transactions[1].Inputs[0].PrevOut.N); // Transaction[1] outputs. Assert.Equal(3, transactions[1].Outputs.Count); // Transaction[1] output value - change. Assert.Equal(new Money(10m, MoneyUnit.BTC), transactions[1].Outputs[0].Value); Assert.Equal(multiSigAddress.ScriptPubKey, transactions[1].Outputs[0].ScriptPubKey); // Transaction[1] output value - recipient 2. Assert.Equal(new Money(59.99m, MoneyUnit.BTC), transactions[1].Outputs[1].Value); Assert.Equal(address2.ScriptPubKey, transactions[1].Outputs[1].ScriptPubKey); // Transaction[1] output value - op_return. Assert.Equal(new Money(0m, MoneyUnit.BTC), transactions[1].Outputs[2].Value); new OpReturnDataReader(this.loggerFactory, this.federatedPegOptions).TryGetTransactionId(transactions[1], out string actualDepositId2); Assert.Equal(deposit2.Id.ToString(), actualDepositId2); ICrossChainTransfer[] transfers = crossChainTransferStore.GetAsync(new uint256[] { 0, 1 }).GetAwaiter().GetResult().ToArray(); Assert.Equal(2, transfers.Length); Assert.Equal(CrossChainTransferStatus.Partial, transfers[0].Status); Assert.Equal(deposit1.Amount, new Money(transfers[0].DepositAmount)); Assert.Equal(address1.ScriptPubKey, transfers[0].DepositTargetAddress); Assert.Equal(CrossChainTransferStatus.Partial, transfers[1].Status); Assert.Equal(deposit2.Amount, new Money(transfers[1].DepositAmount)); Assert.Equal(address2.ScriptPubKey, transfers[1].DepositTargetAddress); } }
public void StoringDepositsAfterRewindIsPrecededByClearingInvalidTransientsAndSettingNextMatureDepositHeightCorrectly() { var dataFolder = new DataFolder(TestBase.CreateTestDir(this)); this.Init(dataFolder); // Creates two consecutive blocks of funding transactions with 100 coins each. (Transaction fundingTransaction1, ChainedHeader fundingBlock1) = AddFundingTransaction(new Money[] { Money.COIN * 100 }); (Transaction fundingTransaction2, ChainedHeader fundingBlock2) = AddFundingTransaction(new Money[] { Money.COIN * 100 }); this.AppendBlocks(WithdrawalTransactionBuilder.MinConfirmations); MultiSigAddress multiSigAddress = this.wallet.MultiSigAddress; using (ICrossChainTransferStore crossChainTransferStore = this.CreateStore()) { crossChainTransferStore.Initialize(); crossChainTransferStore.Start(); TestBase.WaitLoopMessage(() => (this.ChainIndexer.Tip.Height == crossChainTransferStore.TipHashAndHeight.Height, $"ChainIndexer.Height:{this.ChainIndexer.Tip.Height} Store.TipHashHeight:{crossChainTransferStore.TipHashAndHeight.Height}")); Assert.Equal(this.ChainIndexer.Tip.HashBlock, crossChainTransferStore.TipHashAndHeight.HashBlock); BitcoinAddress address1 = (new Key()).PubKey.Hash.GetAddress(this.network); BitcoinAddress address2 = (new Key()).PubKey.Hash.GetAddress(this.network); // First deposit. var deposit1 = new Deposit(1, new Money(100m, MoneyUnit.BTC), address1.ToString(), crossChainTransferStore.NextMatureDepositHeight, 1); MaturedBlockDepositsModel[] blockDeposit1 = new[] { new MaturedBlockDepositsModel( new MaturedBlockInfoModel() { BlockHash = 1, BlockHeight = crossChainTransferStore.NextMatureDepositHeight }, new[] { deposit1 }) }; crossChainTransferStore.RecordLatestMatureDepositsAsync(blockDeposit1).GetAwaiter().GetResult(); ICrossChainTransfer transfer1 = crossChainTransferStore.GetAsync(new[] { deposit1.Id }).GetAwaiter().GetResult().FirstOrDefault(); Assert.Equal(CrossChainTransferStatus.Partial, transfer1?.Status); // Second deposit. var deposit2 = new Deposit(2, new Money(100m, MoneyUnit.BTC), address2.ToString(), crossChainTransferStore.NextMatureDepositHeight, 2); MaturedBlockDepositsModel[] blockDeposit2 = new[] { new MaturedBlockDepositsModel( new MaturedBlockInfoModel() { BlockHash = 2, BlockHeight = crossChainTransferStore.NextMatureDepositHeight }, new[] { deposit2 }) }; crossChainTransferStore.RecordLatestMatureDepositsAsync(blockDeposit2).GetAwaiter().GetResult(); ICrossChainTransfer transfer2 = crossChainTransferStore.GetAsync(new[] { deposit2.Id }).GetAwaiter().GetResult().FirstOrDefault(); Assert.Equal(CrossChainTransferStatus.Partial, transfer2?.Status); // Both partial transactions have been created. Now rewind the wallet. this.ChainIndexer.SetTip(fundingBlock1); this.federationWalletSyncManager.ProcessBlock(fundingBlock1.Block); TestBase.WaitLoopMessage(() => (this.ChainIndexer.Tip.Height == this.federationWalletSyncManager.WalletTip.Height, $"ChainIndexer.Height:{this.ChainIndexer.Tip.Height} SyncManager.TipHashHeight:{this.federationWalletSyncManager.WalletTip.Height}")); // Synchronize the store using a dummy get. crossChainTransferStore.GetAsync(new uint256[] { }).GetAwaiter().GetResult(); // See if the NextMatureDepositHeight was rewound for the replay of deposit 2. Assert.Equal(deposit2.BlockNumber, crossChainTransferStore.NextMatureDepositHeight); // That's great. Now let's redo deposit 2 which had its funding wiped out. (fundingTransaction2, fundingBlock2) = AddFundingTransaction(new Money[] { Money.COIN * 100 }); // Ensure that the new funds are mature. this.AppendBlocks(WithdrawalTransactionBuilder.MinConfirmations); // Recreate the second deposit. crossChainTransferStore.RecordLatestMatureDepositsAsync(blockDeposit2).GetAwaiter().GetResult(); // Check that its status is partial. transfer2 = crossChainTransferStore.GetAsync(new[] { deposit2.Id }).GetAwaiter().GetResult().FirstOrDefault(); Assert.Equal(CrossChainTransferStatus.Partial, transfer2?.Status); Assert.Equal(2, this.wallet.MultiSigAddress.Transactions.Count); } }
public void StoringDepositsWhenWalletBalanceInSufficientSucceedsWithSuspendStatus() { var dataFolder = new DataFolder(TestBase.CreateTestDir(this)); this.Init(dataFolder); this.AddFunding(); this.AppendBlocks(WithdrawalTransactionBuilder.MinConfirmations); MultiSigAddress multiSigAddress = this.wallet.MultiSigAddress; using (ICrossChainTransferStore crossChainTransferStore = this.CreateStore()) { crossChainTransferStore.Initialize(); crossChainTransferStore.Start(); TestBase.WaitLoopMessage(() => (this.ChainIndexer.Tip.Height == crossChainTransferStore.TipHashAndHeight.Height, $"ChainIndexer.Height:{this.ChainIndexer.Tip.Height} Store.TipHashHeight:{crossChainTransferStore.TipHashAndHeight.Height}")); TestBase.WaitLoop(() => this.wallet.LastBlockSyncedHeight == this.ChainIndexer.Tip.Height); Assert.Equal(this.ChainIndexer.Tip.HashBlock, crossChainTransferStore.TipHashAndHeight.HashBlock); uint256 txId1 = 0; uint256 txId2 = 1; uint256 blockHash = 2; BitcoinAddress address1 = (new Key()).PubKey.Hash.GetAddress(this.network); BitcoinAddress address2 = (new Key()).PubKey.Hash.GetAddress(this.network); var deposit1 = new Deposit(txId1, new Money(160m, MoneyUnit.BTC), address1.ToString(), crossChainTransferStore.NextMatureDepositHeight, blockHash); var deposit2 = new Deposit(txId2, new Money(100m, MoneyUnit.BTC), address2.ToString(), crossChainTransferStore.NextMatureDepositHeight, blockHash); MaturedBlockDepositsModel[] blockDeposits = new[] { new MaturedBlockDepositsModel( new MaturedBlockInfoModel() { BlockHash = blockHash, BlockHeight = crossChainTransferStore.NextMatureDepositHeight }, new[] { deposit1, deposit2 }) }; crossChainTransferStore.RecordLatestMatureDepositsAsync(blockDeposits).GetAwaiter().GetResult(); ICrossChainTransfer[] transfers = crossChainTransferStore.GetAsync(new uint256[] { txId1, txId2 }).GetAwaiter().GetResult().ToArray(); Transaction[] transactions = transfers.Select(t => t.PartialTransaction).ToArray(); Assert.Equal(2, transactions.Length); // Transactions[0] inputs. Ordered deterministically. Assert.Equal(2, transactions[0].Inputs.Count); Assert.Equal(this.fundingTransactions[0].GetHash(), transactions[0].Inputs[0].PrevOut.Hash); Assert.Equal((uint)0, transactions[0].Inputs[0].PrevOut.N); Assert.Equal(this.fundingTransactions[0].GetHash(), transactions[0].Inputs[1].PrevOut.Hash); Assert.Equal((uint)1, transactions[0].Inputs[1].PrevOut.N); // Transaction[0] outputs. Assert.Equal(3, transactions[0].Outputs.Count); // Transaction[0] output value - change. Assert.Equal(new Money(10m, MoneyUnit.BTC), transactions[0].Outputs[0].Value); Assert.Equal(multiSigAddress.ScriptPubKey, transactions[0].Outputs[0].ScriptPubKey); // Transaction[0] output value - recipient 1, but minus 0.01 for the tx fee. Assert.Equal(new Money(159.99m, MoneyUnit.BTC), transactions[0].Outputs[1].Value); Assert.Equal(address1.ScriptPubKey, transactions[0].Outputs[1].ScriptPubKey); // Transaction[0] output value - op_return. Assert.Equal(new Money(0m, MoneyUnit.BTC), transactions[0].Outputs[2].Value); new OpReturnDataReader(this.loggerFactory, this.federatedPegOptions).TryGetTransactionId(transactions[0], out string actualDepositId); Assert.Equal(deposit1.Id.ToString(), actualDepositId); Assert.Null(transactions[1]); Assert.Equal(2, transfers.Length); Assert.Equal(CrossChainTransferStatus.Partial, transfers[0].Status); Assert.Equal(deposit1.Amount, new Money(transfers[0].DepositAmount)); Assert.Equal(address1.ScriptPubKey, transfers[0].DepositTargetAddress); Assert.Equal(CrossChainTransferStatus.Suspended, transfers[1].Status); // Add more funds and resubmit the deposits. AddFundingTransaction(new Money[] { Money.COIN * 1000 }); TestBase.WaitLoop(() => this.wallet.LastBlockSyncedHeight == this.ChainIndexer.Tip.Height); crossChainTransferStore.RecordLatestMatureDepositsAsync(blockDeposits).GetAwaiter().GetResult(); transfers = crossChainTransferStore.GetAsync(new uint256[] { txId1, txId2 }).GetAwaiter().GetResult().ToArray(); transactions = transfers.Select(t => t.PartialTransaction).ToArray(); // Transactions[1] inputs. Assert.Equal(2, transactions[1].Inputs.Count); Assert.Equal(this.fundingTransactions[1].GetHash(), transactions[1].Inputs[0].PrevOut.Hash); Assert.Equal((uint)0, transactions[1].Inputs[0].PrevOut.N); // Transaction[1] outputs. Assert.Equal(3, transactions[1].Outputs.Count); // Transaction[1] output value - change. Assert.Equal(new Money(970m, MoneyUnit.BTC), transactions[1].Outputs[0].Value); Assert.Equal(multiSigAddress.ScriptPubKey, transactions[1].Outputs[0].ScriptPubKey); // Transaction[1] output value - recipient 2, but minus 0.01 for the tx fee. Assert.Equal(new Money(99.99m, MoneyUnit.BTC), transactions[1].Outputs[1].Value); Assert.Equal(address2.ScriptPubKey, transactions[1].Outputs[1].ScriptPubKey); // Transaction[1] output value - op_return. Assert.Equal(new Money(0m, MoneyUnit.BTC), transactions[1].Outputs[2].Value); new OpReturnDataReader(this.loggerFactory, this.federatedPegOptions).TryGetTransactionId(transactions[1], out string actualDepositId2); Assert.Equal(deposit2.Id.ToString(), actualDepositId2); Assert.Equal(2, transfers.Length); Assert.Equal(CrossChainTransferStatus.Partial, transfers[1].Status); Assert.Equal(deposit2.Amount, new Money(transfers[1].DepositAmount)); Assert.Equal(address2.ScriptPubKey, transfers[1].DepositTargetAddress); (Money confirmed, Money unconfirmed)spendable = this.wallet.GetSpendableAmount(); Assert.Equal(new Money(980m, MoneyUnit.BTC), spendable.unconfirmed); } }