diff --git a/README.md b/README.md
index 5d3f25e..fff2727 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,5 @@
-# Unity3dIntegration
\ No newline at end of file
+# Unity3d_Stratis
+
+
+
+Unity3d client for Stratis.
diff --git a/Resources/StratisUnity3d_v1.unitypackage b/Resources/StratisUnity3d_v1.unitypackage
new file mode 100644
index 0000000..13d2385
Binary files /dev/null and b/Resources/StratisUnity3d_v1.unitypackage differ
diff --git a/Src/StratisUnity3d/.gitignore b/Src/StratisUnity3d/.gitignore
new file mode 100644
index 0000000..20d11c9
--- /dev/null
+++ b/Src/StratisUnity3d/.gitignore
@@ -0,0 +1,59 @@
+# This .gitignore file should be placed at the root of your Unity project directory
+#
+# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
+#
+/[Ll]ibrary/
+/[Tt]emp/
+/[Oo]bj/
+/[Bb]uild/
+/[Bb]uilds/
+/[Ll]ogs/
+/[Mm]emoryCaptures/
+
+# Asset meta data should only be ignored when the corresponding asset is also ignored
+!/[Aa]ssets/**/*.meta
+
+# Uncomment this line if you wish to ignore the asset store tools plugin
+# /[Aa]ssets/AssetStoreTools*
+
+# Autogenerated Jetbrains Rider plugin
+[Aa]ssets/Plugins/Editor/JetBrains*
+
+# Visual Studio cache directory
+.vs/
+
+# Gradle cache directory
+.gradle/
+
+# Autogenerated VS/MD/Consulo solution and project files
+ExportedObj/
+.consulo/
+*.csproj
+*.unityproj
+*.sln
+*.suo
+*.tmp
+*.user
+*.userprefs
+*.pidb
+*.booproj
+*.svd
+*.pdb
+*.mdb
+*.opendb
+*.VC.db
+
+# Unity3D generated meta files
+*.pidb.meta
+*.pdb.meta
+*.mdb.meta
+
+# Unity3D generated file on crash reports
+sysinfo.txt
+
+# Builds
+*.apk
+*.unitypackage
+
+# Crashlytics generated file
+crashlytics-build.properties
\ No newline at end of file
diff --git a/Src/StratisUnity3d/.vsconfig b/Src/StratisUnity3d/.vsconfig
new file mode 100644
index 0000000..aade28f
--- /dev/null
+++ b/Src/StratisUnity3d/.vsconfig
@@ -0,0 +1,6 @@
+{
+ "version": "1.0",
+ "components": [
+ "Microsoft.VisualStudio.Workload.ManagedGame"
+ ]
+}
diff --git a/Src/StratisUnity3d/Assets/Code.meta b/Src/StratisUnity3d/Assets/Code.meta
new file mode 100644
index 0000000..b3e00a7
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8b7d8f89e96b0b249a328fc5ee61eccc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies.meta b/Src/StratisUnity3d/Assets/Code/Dependencies.meta
new file mode 100644
index 0000000..b73509f
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 39379ec8e925e904c993a7d744cee18f
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/CirrusNetwork.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/CirrusNetwork.cs
new file mode 100644
index 0000000..ef4171b
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/CirrusNetwork.cs
@@ -0,0 +1,46 @@
+using NBitcoin;
+using NBitcoin.DataEncoders;
+
+namespace Stratis.Sidechains.Networks
+{
+ public static class CirrusNetwork
+ {
+ /// The name of the root folder containing the different side chain block data.
+ public const string NetworkRootFolderName = "cirrus";
+
+ /// The default name used for the side chain configuration file.
+ public const string NetworkDefaultConfigFilename = "cirrus.conf";
+
+ public static Block CreateGenesis(ConsensusFactory consensusFactory, uint genesisTime, uint nonce, uint bits, int version, Money reward, string coinbaseText)
+ {
+ Transaction genesisTransaction = consensusFactory.CreateTransaction();
+ // TODO: Remove Cirrus networks from this solution?
+ //genesisTransaction.Time = genesisTime;
+ genesisTransaction.Version = 1;
+ genesisTransaction.AddInput(new TxIn()
+ {
+ ScriptSig = new Script(Op.GetPushOp(0), new Op()
+ {
+ Code = (OpcodeType)0x1,
+ PushData = new[] { (byte)42 }
+ }, Op.GetPushOp(Encoders.ASCII.DecodeData(coinbaseText)))
+ });
+
+ genesisTransaction.AddOutput(new TxOut()
+ {
+ Value = reward
+ });
+
+ Block genesis = consensusFactory.CreateBlock();
+ genesis.Header.BlockTime = Utils.UnixTimeToDateTime(genesisTime);
+ genesis.Header.Bits = bits;
+ genesis.Header.Nonce = nonce;
+ genesis.Header.Version = version;
+ genesis.Transactions.Add(genesisTransaction);
+ genesis.Header.HashPrevBlock = uint256.Zero;
+ genesis.UpdateMerkleRoot();
+
+ return genesis;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/CirrusNetwork.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/CirrusNetwork.cs.meta
new file mode 100644
index 0000000..42a91ca
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/CirrusNetwork.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d91e3db3358853b458f03f915ff117ae
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks.meta
new file mode 100644
index 0000000..2f50bd3
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 55d83bd9edcf7ad41af61889b664fcdf
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs
new file mode 100644
index 0000000..12623ec
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using NBitcoin;
+using NBitcoin.DataEncoders;
+
+namespace Stratis.Sidechains.Networks
+{
+ public class CirrusMain : Network
+ {
+ public CirrusMain()
+ {
+ this.Name = "CirrusMain";
+ this.NetworkType = NetworkType.Mainnet;
+ this.CoinTicker = "CRS";
+ this.Magic = 0x522357AC;
+ this.DefaultPort = 16179;
+ this.DefaultMaxOutboundConnections = 16;
+ this.DefaultMaxInboundConnections = 109;
+ this.DefaultRPCPort = 16175;
+ this.DefaultAPIPort = 37223;
+ this.DefaultSignalRPort = 38823;
+ this.MaxTipAge = 768; // 20% of the fastest time it takes for one MaxReorgLength of blocks to be mined.
+ this.MinTxFee = 10000;
+ this.FallbackFee = 10000;
+ this.MinRelayTxFee = 10000;
+ this.RootFolderName = CirrusNetwork.NetworkRootFolderName;
+ this.DefaultConfigFilename = CirrusNetwork.NetworkDefaultConfigFilename;
+ this.MaxTimeOffsetSeconds = 25 * 60;
+ this.DefaultBanTimeSeconds = 1920; // 240 (MaxReorg) * 16 (TargetSpacing) / 2 = 32 Minutes
+
+ this.CirrusRewardDummyAddress = "CPqxvnzfXngDi75xBJKqi4e6YrFsinrJka";
+
+ var consensusFactory = new ConsensusFactory();
+
+ // Create the genesis block.
+ this.GenesisTime = 1561982325;
+ this.GenesisNonce = 3038481;
+ this.GenesisBits = new Target(new uint256("00000fffff000000000000000000000000000000000000000000000000000000"));
+ this.GenesisVersion = 1;
+ this.GenesisReward = Money.Zero;
+
+ string coinbaseText = "https://github.com/stratisproject/StratisBitcoinFullNode";
+ Block genesisBlock = CirrusNetwork.CreateGenesis(consensusFactory, this.GenesisTime, this.GenesisNonce, this.GenesisBits, this.GenesisVersion, this.GenesisReward, coinbaseText);
+
+ this.Genesis = genesisBlock;
+
+ this.Federations = new Federations();
+ var straxFederationTransactionSigningKeys = new List()
+ {
+ new PubKey("03797a2047f84ba7dcdd2816d4feba45ae70a59b3aa97f46f7877df61aa9f06a21"),
+ new PubKey("0209cfca2490dec022f097114090c919e85047de0790c1c97451e0f50c2199a957"),
+ new PubKey("032e4088451c5a7952fb6a862cdad27ea18b2e12bccb718f13c9fdcc1caf0535b4"),
+ new PubKey("035bf78614171397b080c5b375dbb7a5ed2a4e6fb43a69083267c880f66de5a4f9"),
+ new PubKey("02387a219b1de54d4dc73a710a2315d957fc37ab04052a6e225c89205b90a881cd"),
+ new PubKey("028078c0613033e5b4d4745300ede15d87ed339e379daadc6481d87abcb78732fa"),
+ new PubKey("02b3e16d2e4bbad6dba1e699934a52d58d9b60b6e7eed303e400e95f2dbc2ef3fd"),
+ new PubKey("02ba8b842997ce50c8e29c24a5452de5482f1584ae79778950b7bae24d4cc68dad"),
+ new PubKey("02cbd907b0bf4d757dee7ea4c28e63e46af19dc8df0c924ee5570d9457be2f4c73"),
+ new PubKey("02d371f3a0cffffcf5636e6d4b79d9f018a1a18fbf64c39542b382c622b19af9de"),
+ new PubKey("02f891910d28fc26f272da8d7f548fdc18c286704907673e839dc07e8df416c15e"),
+ new PubKey("0337e816a3433c71c4bbc095a54a0715a6da7a70526d2afb8dba3d8d78d33053bf"),
+ new PubKey("035569e42835e25c854daa7de77c20f1009119a5667494664a46b5154db7ee768a"),
+ new PubKey("03cda7ea577e8fbe5d45b851910ec4a795e5cc12d498cf80d39ba1d9a455942188"),
+ new PubKey("02680321118bce869933b07ea42cc04d2a2804134b06db582427d6b9688b3536a4")
+ };
+
+ // Register the new set of federation members.
+ this.Federations.RegisterFederation(new Federation(straxFederationTransactionSigningKeys));
+
+ var buriedDeployments = new BuriedDeploymentsArray
+ {
+ [BuriedDeployments.BIP34] = 0,
+ [BuriedDeployments.BIP65] = 0,
+ [BuriedDeployments.BIP66] = 0
+ };
+
+ var bip9Deployments = new NoBIP9Deployments();
+
+ this.Consensus = new Consensus(
+ consensusFactory: consensusFactory,
+ consensusOptions: null,
+ coinType: 401,
+ hashGenesisBlock: genesisBlock.GetHash(),
+ subsidyHalvingInterval: 210000,
+ majorityEnforceBlockUpgrade: 750,
+ majorityRejectBlockOutdated: 950,
+ majorityWindow: 1000,
+ buriedDeployments: buriedDeployments,
+ bip9Deployments: bip9Deployments,
+ bip34Hash: new uint256("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"),
+ minerConfirmationWindow: 2016, // nPowTargetTimespan / nPowTargetSpacing
+ maxReorgLength: 240, // Heuristic. Roughly 2 * mining members
+ defaultAssumeValid: new uint256("0xbfd4a96a6c5250f18bf7c586761256fa5f8753ffa10b24160f0648a452823a95"), // 1400000
+ maxMoney: Money.Coins(100_000_000),
+ coinbaseMaturity: 1,
+ premineHeight: 2,
+ premineReward: Money.Coins(100_000_000),
+ proofOfWorkReward: Money.Coins(0),
+ powTargetTimespan: TimeSpan.FromDays(14), // two weeks
+ targetSpacing: TimeSpan.FromSeconds(16),
+ powAllowMinDifficultyBlocks: false,
+ posNoRetargeting: false,
+ powNoRetargeting: true,
+ powLimit: null,
+ minimumChainWork: null,
+ isProofOfStake: false,
+ lastPowBlock: 0,
+ proofOfStakeLimit: null,
+ proofOfStakeLimitV2: null,
+ proofOfStakeReward: Money.Zero
+ );
+
+ // Same as current smart contracts test networks to keep tests working
+ this.Base58Prefixes = new byte[12][];
+ this.Base58Prefixes[(int)Base58Type.PUBKEY_ADDRESS] = new byte[] { 28 }; // C
+ this.Base58Prefixes[(int)Base58Type.SCRIPT_ADDRESS] = new byte[] { 88 }; // c
+ this.Base58Prefixes[(int)Base58Type.SECRET_KEY] = new byte[] { (239) };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_NO_EC] = new byte[] { 0x01, 0x42 };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_EC] = new byte[] { 0x01, 0x43 };
+ this.Base58Prefixes[(int)Base58Type.EXT_PUBLIC_KEY] = new byte[] { (0x04), (0x35), (0x87), (0xCF) };
+ this.Base58Prefixes[(int)Base58Type.EXT_SECRET_KEY] = new byte[] { (0x04), (0x35), (0x83), (0x94) };
+ this.Base58Prefixes[(int)Base58Type.PASSPHRASE_CODE] = new byte[] { 0x2C, 0xE9, 0xB3, 0xE1, 0xFF, 0x39, 0xE2 };
+ this.Base58Prefixes[(int)Base58Type.CONFIRMATION_CODE] = new byte[] { 0x64, 0x3B, 0xF6, 0xA8, 0x9A };
+ this.Base58Prefixes[(int)Base58Type.STEALTH_ADDRESS] = new byte[] { 0x2b };
+ this.Base58Prefixes[(int)Base58Type.ASSET_ID] = new byte[] { 115 };
+ this.Base58Prefixes[(int)Base58Type.COLORED_ADDRESS] = new byte[] { 0x13 };
+
+ Bech32Encoder encoder = Encoders.Bech32("tb");
+ this.Bech32Encoders = new Bech32Encoder[2];
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_PUBKEY_ADDRESS] = encoder;
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_SCRIPT_ADDRESS] = encoder;
+
+ this.StandardScriptsRegistry = new PoAStandardScriptsRegistry();
+
+ Assert(this.DefaultBanTimeSeconds <= this.Consensus.MaxReorgLength * this.Consensus.TargetSpacing.TotalSeconds / 2);
+ Assert(this.Consensus.HashGenesisBlock == uint256.Parse("000005769503496300ec879afd7543dc9f86d3b3d679950b2b83e2f49f525856"));
+ Assert(this.Genesis.Header.HashMerkleRoot == uint256.Parse("1669a55d45b642af0ce82c5884cf5b8d8efd5bdcb9a450c95f442b9bd1ff65ea"));
+ }
+ }
+
+ ///
+ /// PoA-specific standard transaction definitions.
+ ///
+ public class PoAStandardScriptsRegistry : StandardScriptsRegistry
+ {
+ public const int MaxOpReturnRelay = 153;
+
+ private static readonly List scriptTemplates = new List
+ {
+ { new PayToPubkeyHashTemplate() },
+ { new PayToPubkeyTemplate() },
+ { new PayToScriptHashTemplate() },
+ { new PayToMultiSigTemplate() },
+ { new PayToFederationTemplate() },
+ { new TxNullDataTemplate(MaxOpReturnRelay) },
+ { new PayToWitTemplate() }
+ };
+
+ public override List GetScriptTemplates => scriptTemplates;
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs.meta
new file mode 100644
index 0000000..8b66178
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: aefd078ec0d58a34c996714f87a80b00
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs
new file mode 100644
index 0000000..a2dcba0
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using NBitcoin;
+using NBitcoin.DataEncoders;
+using NBitcoin.Protocol;
+
+namespace Stratis.Sidechains.Networks
+{
+ public class CirrusTest : Network
+ {
+ public CirrusTest()
+ {
+ this.Name = "CirrusTest";
+ this.NetworkType = NetworkType.Testnet;
+ this.CoinTicker = "TCRS";
+ this.Magic = 0x522357B;
+ this.DefaultPort = 26179;
+ this.DefaultMaxOutboundConnections = 16;
+ this.DefaultMaxInboundConnections = 109;
+ this.DefaultRPCPort = 26175;
+ this.DefaultAPIPort = 38223;
+ this.DefaultSignalRPort = 39823;
+ this.MaxTipAge = 768; // 20% of the fastest time it takes for one MaxReorgLength of blocks to be mined.
+ this.MinTxFee = 10000;
+ this.FallbackFee = 10000;
+ this.MinRelayTxFee = 10000;
+ this.MaxTimeOffsetSeconds = 25 * 60;
+ this.DefaultBanTimeSeconds = 1920; // 240 (MaxReorg) * 16 (TargetSpacing) / 2 = 32 Minutes
+
+ this.CirrusRewardDummyAddress = "tGXZrZiU44fx3SQj8tAQ3Zexy2VuELZtoh";
+
+ var consensusFactory = new ConsensusFactory();
+
+ // Create the genesis block.
+ this.GenesisTime = 1556631753;
+ this.GenesisNonce = 146421;
+ this.GenesisBits = new Target(new uint256("0000ffff00000000000000000000000000000000000000000000000000000000"));
+ this.GenesisVersion = 1;
+ this.GenesisReward = Money.Zero;
+
+ string coinbaseText = "https://github.com/stratisproject/StratisBitcoinFullNode/tree/master/src/Stratis.CirrusD";
+ Block genesisBlock = CirrusNetwork.CreateGenesis(consensusFactory, this.GenesisTime, this.GenesisNonce, this.GenesisBits, this.GenesisVersion, this.GenesisReward, coinbaseText);
+
+ this.Genesis = genesisBlock;
+
+ this.Federations = new Federations();
+ var straxFederationTransactionSigningKeys = new List()
+ {
+ new PubKey("021040ef28c82fcffb63028e69081605ed4712910c8384d5115c9ffeacd9dbcae4"),//Node1
+ new PubKey("0244290a31824ba7d53e59c7a29d13dbeca15a9b0d36fdd4d28fce426753107bfc"),//Node2
+ new PubKey("032df4a2d62c0db12cd1d66201819a10788637c9b90a1cd2a5a3f5196fdab7a621"),//Node3
+ new PubKey("028ed190eb4ed6e46440ac6af21d8a67a537bd1bd7edb9cc5177d36d5a0972244d"),//Node4
+ new PubKey("02ff9923324399a188daf4310825a85dd3b89e2301d0ad073295b6f33ae1c72f7a"),//Node5
+ new PubKey("030e03b808ddb51701d4d3dbc0a74a6f9aedfecf23d5f874914641fc81197b239a"),//Node7
+ new PubKey("02270d6c20d3393fad7f74c59d2d26b0824ed016ccbc15e698e7354314459a60a5"),//Node8
+ };
+
+ // Register the new set of federation members.
+ this.Federations.RegisterFederation(new Federation(straxFederationTransactionSigningKeys));
+
+ var buriedDeployments = new BuriedDeploymentsArray
+ {
+ [BuriedDeployments.BIP34] = 0,
+ [BuriedDeployments.BIP65] = 0,
+ [BuriedDeployments.BIP66] = 0
+ };
+
+ var bip9Deployments = new NoBIP9Deployments();
+
+ this.Consensus = new Consensus(
+ consensusFactory: consensusFactory,
+ consensusOptions: null,
+ coinType: 400,
+ hashGenesisBlock: genesisBlock.GetHash(),
+ subsidyHalvingInterval: 210000,
+ majorityEnforceBlockUpgrade: 750,
+ majorityRejectBlockOutdated: 950,
+ majorityWindow: 1000,
+ buriedDeployments: buriedDeployments,
+ bip9Deployments: bip9Deployments,
+ bip34Hash: new uint256("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"),
+ minerConfirmationWindow: 2016, // nPowTargetTimespan / nPowTargetSpacing
+ maxReorgLength: 240, // Heuristic. Roughly 2 * mining members
+ defaultAssumeValid: new uint256("0x57a3119de52cf43b66d6e805a644c20fdee63557038cd68c429d47b21d111084"), // 1800000
+ maxMoney: Money.Coins(20_000_000),
+ coinbaseMaturity: 1,
+ premineHeight: 2,
+ premineReward: Money.Coins(20_000_000),
+ proofOfWorkReward: Money.Coins(0),
+ powTargetTimespan: TimeSpan.FromDays(14), // two weeks
+ targetSpacing: TimeSpan.FromSeconds(16),
+ powAllowMinDifficultyBlocks: false,
+ posNoRetargeting: false,
+ powNoRetargeting: true,
+ powLimit: null,
+ minimumChainWork: null,
+ isProofOfStake: false,
+ lastPowBlock: 0,
+ proofOfStakeLimit: null,
+ proofOfStakeLimitV2: null,
+ proofOfStakeReward: Money.Zero
+ );
+
+ // Same as current smart contracts test networks to keep tests working
+ this.Base58Prefixes = new byte[12][];
+ this.Base58Prefixes[(int)Base58Type.PUBKEY_ADDRESS] = new byte[] { 127 }; // t
+ this.Base58Prefixes[(int)Base58Type.SCRIPT_ADDRESS] = new byte[] { 137 }; // x
+ this.Base58Prefixes[(int)Base58Type.SECRET_KEY] = new byte[] { (239) };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_NO_EC] = new byte[] { 0x01, 0x42 };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_EC] = new byte[] { 0x01, 0x43 };
+ this.Base58Prefixes[(int)Base58Type.EXT_PUBLIC_KEY] = new byte[] { (0x04), (0x35), (0x87), (0xCF) };
+ this.Base58Prefixes[(int)Base58Type.EXT_SECRET_KEY] = new byte[] { (0x04), (0x35), (0x83), (0x94) };
+ this.Base58Prefixes[(int)Base58Type.PASSPHRASE_CODE] = new byte[] { 0x2C, 0xE9, 0xB3, 0xE1, 0xFF, 0x39, 0xE2 };
+ this.Base58Prefixes[(int)Base58Type.CONFIRMATION_CODE] = new byte[] { 0x64, 0x3B, 0xF6, 0xA8, 0x9A };
+ this.Base58Prefixes[(int)Base58Type.STEALTH_ADDRESS] = new byte[] { 0x2b };
+ this.Base58Prefixes[(int)Base58Type.ASSET_ID] = new byte[] { 115 };
+ this.Base58Prefixes[(int)Base58Type.COLORED_ADDRESS] = new byte[] { 0x13 };
+
+ Bech32Encoder encoder = Encoders.Bech32("tb");
+ this.Bech32Encoders = new Bech32Encoder[2];
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_PUBKEY_ADDRESS] = encoder;
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_SCRIPT_ADDRESS] = encoder;
+
+ this.Checkpoints = new Dictionary()
+ {
+ { 50000, new CheckpointInfo(new uint256("0x2b2a85fcad21c4e5c91a7afef04dce2eb72426b0c6275d87669a561f9f6db1f3")) },
+ { 100000, new CheckpointInfo(new uint256("0x364be98c01780accfea63c52703b7dc4731fdead1b6769cf9a893b4e6c736f10")) },
+ { 150000, new CheckpointInfo(new uint256("0xaf862418d54d95221dac077cdbd0d49d68304d67721df7b44775739f093985f1")) },
+ { 200000, new CheckpointInfo(new uint256("0x40f99ccbd290c2c66c16eac602b4a8b4dc7d87bfceb31c64ae5942d5899e86b2")) },
+ { 250000, new CheckpointInfo(new uint256("0x33deee954579b8b3ffde1d9246a3e9e548dc7f4f8c0c9cbf206eb14ac04ab500")) },
+ { 300000, new CheckpointInfo(new uint256("0x1c1670f9ea4d211abe255a516be95ec6329d03e0ebfc81890cae0900e9f07964")) },
+ { 350000, new CheckpointInfo(new uint256("0x5b3493243a9f8c8997acad7cb13058e11a9d2d91c9494ebe5c88446540640472")) },
+ { 400000, new CheckpointInfo(new uint256("0x33d57af0bc04916eb43f6d5c3f0b97b0f281662feac3d03b987bb9ab4978fe0a")) },
+ { 450000, new CheckpointInfo(new uint256("0x7c85cc3aa0694c7573b1455e555c9f6a919dfa916381d6c094cdc2da46a0c7bc")) },
+ { 500000, new CheckpointInfo(new uint256("0x3f00eb415856128976e786cb094e88d4dfaabfedea462498386e201c1ac2a1fa")) },
+ { 550000, new CheckpointInfo(new uint256("0xaaf247bd66568db8945fc8947525539160073bcfb4a60a09d23fdcbf4d775a15")) },
+ { 600000, new CheckpointInfo(new uint256("0x610a60579898e9160509ea4453cb946e1fdb9ebc18eedffd77513f42a61c0d77")) },
+ { 700000, new CheckpointInfo(new uint256("0x6d5addc975a93eb323933bcdf2c3b7e098e324e8b205232a490cd585aceb1518")) },
+ { 800000, new CheckpointInfo(new uint256("0x6ff2a00696e1601efba88b98ef63e691e8da7acffd5703614e971c932d93af80")) },
+ { 900000, new CheckpointInfo(new uint256("0x84b550eafbfe777d28321eabed9a118a3175bcd607481bbfe24dc5fa2a9de0cf")) },
+ { 1_000_000, new CheckpointInfo(new uint256("0xc3da5b782bdf6b9d0606147996479b0ea621322d9df1d239cbbd814175f4ed61")) },
+ { 1_100_000, new CheckpointInfo(new uint256("0xbb2d946fa7101c14c6374b0e40993ef73401a360e74652e1677d8d6b3b4be01c")) },
+ { 1_200_000, new CheckpointInfo(new uint256("0x8b7c48e0e814afbedb0d6e67dc71aaa395886db58e17fd622e571e1d140fbbb3")) },
+ { 1_300_000, new CheckpointInfo(new uint256("0xe4aecd9ecdbf4e55b08255ed6d8a98e811fbd3e7c72ef267c26ebfae4e315990")) },
+ { 1_400_000, new CheckpointInfo(new uint256("0x7165b03c170869b318253d470aa904f9c674c0d0f4ca2e9a64416b1d42beecc5")) },
+ { 1_500_000, new CheckpointInfo(new uint256("0xb458117f195f936d7767f7299d0976ad90700e321870c18ec1e3481924f2afc3")) },
+ { 1_600_000, new CheckpointInfo(new uint256("0x696cd64ec08b67ed3a3ec1e3add77c0e8203d8d6c0bb7df96dd9508dda4ba67e")) },
+ { 1_700_000, new CheckpointInfo(new uint256("0xf42564107701d81e847e5dc6bd95da6bf32cb54e762d84118a7764349b414e68")) },
+ { 1_800_000, new CheckpointInfo(new uint256("0x57a3119de52cf43b66d6e805a644c20fdee63557038cd68c429d47b21d111084")) },
+ { 1_900_000, new CheckpointInfo(new uint256("0xd413f3aed50f4a1a4580e7c506223a605e222849da9649ca6d43ad7aac5c5af5")) },
+ { 2_050_000, new CheckpointInfo(new uint256("0x543511cdefc38ee4fc272872543427cf08c6406ab602799b47138e418aa195fc")) },
+ };
+
+ this.DNSSeeds = new List
+ {
+ new DNSSeedData("cirrustest1.stratisnetwork.com", "cirrustest1.stratisnetwork.com")
+ };
+
+ this.SeedNodes = new List();
+ this.StandardScriptsRegistry = new PoAStandardScriptsRegistry();
+
+
+ Assert(this.DefaultBanTimeSeconds <= this.Consensus.MaxReorgLength * this.Consensus.TargetSpacing.TotalSeconds / 2);
+ Assert(this.Consensus.HashGenesisBlock == uint256.Parse("0000af9ab2c8660481328d0444cf167dfd31f24ca2dbba8e5e963a2434cffa93"));
+ Assert(this.Genesis.Header.HashMerkleRoot == uint256.Parse("cf8ce1419bbc4870b7d4f1c084534d91126dd3283b51ec379e0a20e27bd23633"));
+ }
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs.meta
new file mode 100644
index 0000000..620097f
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5fa9e497b7f95a348ad04ac3472e97cf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxMain.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxMain.cs
new file mode 100644
index 0000000..6bc1016
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxMain.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NBitcoin;
+using NBitcoin.BouncyCastle.Math;
+using NBitcoin.DataEncoders;
+
+namespace Stratis.Bitcoin.Networks
+{
+ public class StraxMain : Network
+ {
+ public StraxMain()
+ {
+ this.Name = "StraxMain";
+ this.NetworkType = NetworkType.Mainnet;
+ this.Magic = BitConverter.ToUInt32(Encoding.ASCII.GetBytes("StrX"), 0);
+ this.DefaultPort = 17105;
+ this.DefaultMaxOutboundConnections = 16;
+ this.DefaultMaxInboundConnections = 109;
+ this.DefaultRPCPort = 17104;
+ this.DefaultAPIPort = 17103;
+ this.DefaultSignalRPort = 17102;
+ this.MaxTipAge = 2 * 60 * 60;
+ this.MinTxFee = 10000;
+ this.FallbackFee = 10000;
+ this.MinRelayTxFee = 10000;
+ this.RootFolderName = StraxNetwork.StraxRootFolderName;
+ this.DefaultConfigFilename = StraxNetwork.StraxDefaultConfigFilename;
+ this.MaxTimeOffsetSeconds = 25 * 60;
+ this.CoinTicker = "STRAX";
+ this.DefaultBanTimeSeconds = 11250; // 500 (MaxReorg) * 45 (TargetSpacing) / 2 = 3 hours, 7 minutes and 30 seconds
+
+ this.CirrusRewardDummyAddress = "CPqxvnzfXngDi75xBJKqi4e6YrFsinrJka"; // Cirrus main address
+ this.RewardClaimerBatchActivationHeight = 119_200; // Tuesday, 12 January 2021 9:00:00 AM (Estimated)
+ this.RewardClaimerBlockInterval = 100;
+
+ // To successfully process the OP_FEDERATION opcode the federations should be known.
+ this.Federations = new Federations();
+ this.Federations.RegisterFederation(new Federation(new[]
+ {
+ new PubKey("03797a2047f84ba7dcdd2816d4feba45ae70a59b3aa97f46f7877df61aa9f06a21"),
+ new PubKey("0209cfca2490dec022f097114090c919e85047de0790c1c97451e0f50c2199a957"),
+ new PubKey("032e4088451c5a7952fb6a862cdad27ea18b2e12bccb718f13c9fdcc1caf0535b4"),
+ new PubKey("035bf78614171397b080c5b375dbb7a5ed2a4e6fb43a69083267c880f66de5a4f9"),
+ new PubKey("02387a219b1de54d4dc73a710a2315d957fc37ab04052a6e225c89205b90a881cd"),
+ new PubKey("028078c0613033e5b4d4745300ede15d87ed339e379daadc6481d87abcb78732fa"),
+ new PubKey("02b3e16d2e4bbad6dba1e699934a52d58d9b60b6e7eed303e400e95f2dbc2ef3fd"),
+ new PubKey("02ba8b842997ce50c8e29c24a5452de5482f1584ae79778950b7bae24d4cc68dad"),
+ new PubKey("02cbd907b0bf4d757dee7ea4c28e63e46af19dc8df0c924ee5570d9457be2f4c73"),
+ new PubKey("02d371f3a0cffffcf5636e6d4b79d9f018a1a18fbf64c39542b382c622b19af9de"),
+ new PubKey("02f891910d28fc26f272da8d7f548fdc18c286704907673e839dc07e8df416c15e"),
+ new PubKey("0337e816a3433c71c4bbc095a54a0715a6da7a70526d2afb8dba3d8d78d33053bf"),
+ new PubKey("035569e42835e25c854daa7de77c20f1009119a5667494664a46b5154db7ee768a"),
+ new PubKey("03cda7ea577e8fbe5d45b851910ec4a795e5cc12d498cf80d39ba1d9a455942188"),
+ new PubKey("02680321118bce869933b07ea42cc04d2a2804134b06db582427d6b9688b3536a4")}));
+
+ var consensusFactory = new PosConsensusFactory();
+
+ // Create the genesis block.
+ this.GenesisTime = 1604913812; // ~9 November 2020 - https://www.unixtimestamp.com/
+ this.GenesisNonce = 747342; // Set to 1 until correct value found
+ this.GenesisBits = 0x1e0fffff; // The difficulty target
+ this.GenesisVersion = 536870912; // 'Empty' BIP9 deployments as they are all activated from genesis already
+ this.GenesisReward = Money.Zero;
+
+ Block genesisBlock = StraxNetwork.CreateGenesisBlock(consensusFactory, this.GenesisTime, this.GenesisNonce, this.GenesisBits, this.GenesisVersion, this.GenesisReward, "stratisplatform.com/2020/09/25/introducing-strax/");
+
+ this.Genesis = genesisBlock;
+
+ // Taken from Stratis.
+ var consensusOptions = new PosConsensusOptions(
+ maxBlockBaseSize: 1_000_000,
+ maxStandardVersion: 2,
+ maxStandardTxWeight: 150_000,
+ maxBlockSigopsCost: 20_000,
+ maxStandardTxSigopsCost: 20_000 / 2,
+ witnessScaleFactor: 4
+ );
+
+ var buriedDeployments = new BuriedDeploymentsArray
+ {
+ [BuriedDeployments.BIP34] = 0,
+ [BuriedDeployments.BIP65] = 0,
+ [BuriedDeployments.BIP66] = 0
+ };
+
+ this.Consensus = new NBitcoin.Consensus(
+ consensusFactory: consensusFactory,
+ consensusOptions: consensusOptions,
+ coinType: 105105, // https://github.com/satoshilabs/slips/blob/master/slip-0044.md
+ hashGenesisBlock: genesisBlock.GetHash(),
+ subsidyHalvingInterval: 210000,
+ majorityEnforceBlockUpgrade: 750,
+ majorityRejectBlockOutdated: 950,
+ majorityWindow: 1000,
+ buriedDeployments: buriedDeployments,
+ bip9Deployments: null,
+ bip34Hash: null,
+ minerConfirmationWindow: 2016,
+ maxReorgLength: 500,
+ defaultAssumeValid: null, // TODO: Set this once some checkpoint candidates have elapsed
+ maxMoney: long.MaxValue,
+ coinbaseMaturity: 50,
+ premineHeight: 2,
+ premineReward: Money.Coins(124987850),
+ proofOfWorkReward: Money.Coins(18),
+ powTargetTimespan: TimeSpan.FromSeconds(14 * 24 * 60 * 60),
+ targetSpacing: TimeSpan.FromSeconds(45),
+ powAllowMinDifficultyBlocks: false,
+ posNoRetargeting: false,
+ powNoRetargeting: false,
+ powLimit: new Target(new uint256("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")),
+ minimumChainWork: null,
+ isProofOfStake: true,
+ lastPowBlock: 675,
+ proofOfStakeLimit: new BigInteger(uint256.Parse("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false)),
+ proofOfStakeLimitV2: new BigInteger(uint256.Parse("000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false)),
+ proofOfStakeReward: Money.Coins(18)
+ );
+
+ this.Consensus.PosEmptyCoinbase = false;
+
+ this.Base58Prefixes = new byte[12][];
+ this.Base58Prefixes[(int)Base58Type.PUBKEY_ADDRESS] = new byte[] { 75 }; // X
+ this.Base58Prefixes[(int)Base58Type.SCRIPT_ADDRESS] = new byte[] { 140 }; // y
+ this.Base58Prefixes[(int)Base58Type.SECRET_KEY] = new byte[] { (75 + 128) };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_NO_EC] = new byte[] { 0x01, 0x42 };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_EC] = new byte[] { 0x01, 0x43 };
+ this.Base58Prefixes[(int)Base58Type.EXT_PUBLIC_KEY] = new byte[] { (0x04), (0x88), (0xB2), (0x1E) };
+ this.Base58Prefixes[(int)Base58Type.EXT_SECRET_KEY] = new byte[] { (0x04), (0x88), (0xAD), (0xE4) };
+ this.Base58Prefixes[(int)Base58Type.PASSPHRASE_CODE] = new byte[] { 0x2C, 0xE9, 0xB3, 0xE1, 0xFF, 0x39, 0xE2 };
+ this.Base58Prefixes[(int)Base58Type.CONFIRMATION_CODE] = new byte[] { 0x64, 0x3B, 0xF6, 0xA8, 0x9A };
+ this.Base58Prefixes[(int)Base58Type.STEALTH_ADDRESS] = new byte[] { 0x2a };
+ this.Base58Prefixes[(int)Base58Type.ASSET_ID] = new byte[] { 23 };
+ this.Base58Prefixes[(int)Base58Type.COLORED_ADDRESS] = new byte[] { 0x13 };
+
+ this.Bech32Encoders = new Bech32Encoder[2];
+ var encoder = new Bech32Encoder("strax");
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_PUBKEY_ADDRESS] = encoder;
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_SCRIPT_ADDRESS] = encoder;
+
+ this.StandardScriptsRegistry = new StraxStandardScriptsRegistry();
+
+ Assert(this.DefaultBanTimeSeconds <= this.Consensus.MaxReorgLength * this.Consensus.TargetSpacing.TotalSeconds / 2);
+
+ Assert(this.Consensus.HashGenesisBlock == uint256.Parse("0xebe158d09325c470276619ebc5f7f87c98c0ed4b211c46a17a6457655811d082"));
+ Assert(this.Genesis.Header.HashMerkleRoot == uint256.Parse("0xdd91e99b7ca5eb97d9c41b867762d1f2db412ba4331efb61d138fce5d39b9084"));
+ }
+ }
+
+ ///
+ /// Strax-specific standard transaction definitions.
+ ///
+ public class StraxStandardScriptsRegistry : StandardScriptsRegistry
+ {
+ public const int MaxOpReturnRelay = 83;
+
+ // Need a network-specific version of the template list
+ private static readonly List standardTemplates = new List
+ {
+ new PayToPubkeyHashTemplate(),
+ new PayToPubkeyTemplate(),
+ new PayToScriptHashTemplate(),
+ new PayToMultiSigTemplate(),
+ new PayToFederationTemplate(),
+ new TxNullDataTemplate(MaxOpReturnRelay),
+ new PayToWitTemplate()
+ };
+
+ public override List GetScriptTemplates => standardTemplates;
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxMain.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxMain.cs.meta
new file mode 100644
index 0000000..62733ec
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxMain.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 520d72d80515e5c41873c4273a895edf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxTest.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxTest.cs
new file mode 100644
index 0000000..be68a1c
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxTest.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Text;
+using NBitcoin;
+using NBitcoin.BouncyCastle.Math;
+using NBitcoin.DataEncoders;
+using NBitcoin.Protocol;
+
+namespace Stratis.Bitcoin.Networks
+{
+ public sealed class StraxTest : Network
+ {
+ public StraxTest()
+ {
+ this.Name = "StraxTest";
+ this.NetworkType = NetworkType.Testnet;
+ this.Magic = BitConverter.ToUInt32(Encoding.ASCII.GetBytes("TtrX"), 0);
+ this.DefaultPort = 27105;
+ this.DefaultMaxOutboundConnections = 16;
+ this.DefaultMaxInboundConnections = 109;
+ this.DefaultRPCPort = 27104;
+ this.DefaultAPIPort = 27103;
+ this.DefaultSignalRPort = 27102;
+ this.MaxTipAge = 2 * 60 * 60;
+ this.MinTxFee = 10000;
+ this.FallbackFee = 10000;
+ this.MinRelayTxFee = 10000;
+ this.RootFolderName = StraxNetwork.StraxRootFolderName;
+ this.DefaultConfigFilename = StraxNetwork.StraxDefaultConfigFilename;
+ this.MaxTimeOffsetSeconds = 25 * 60;
+ this.CoinTicker = "TSTRAX";
+ this.DefaultBanTimeSeconds = 11250; // 500 (MaxReorg) * 45 (TargetSpacing) / 2 = 3 hours, 7 minutes and 30 seconds
+
+ this.CirrusRewardDummyAddress = "tGXZrZiU44fx3SQj8tAQ3Zexy2VuELZtoh";
+ this.RewardClaimerBatchActivationHeight = 166200;
+ this.RewardClaimerBlockInterval = 100;
+
+ var consensusFactory = new PosConsensusFactory();
+
+ // Create the genesis block.
+ this.GenesisTime = 1598918400; // 1 September 2020
+ this.GenesisNonce = 109534; // TODO: Update this once the final block is mined
+ this.GenesisBits = new Target(new uint256("0000ffff00000000000000000000000000000000000000000000000000000000")).ToCompact(); // This should be set to the same as the PowLimit
+ this.GenesisVersion = 1;
+ this.GenesisReward = Money.Zero;
+
+ Block genesisBlock = StraxNetwork.CreateGenesisBlock(consensusFactory, this.GenesisTime, this.GenesisNonce, this.GenesisBits, this.GenesisVersion, this.GenesisReward, "teststraxgenesisblock");
+
+ this.Genesis = genesisBlock;
+
+ // Taken from Stratis.
+ var consensusOptions = new PosConsensusOptions(
+ maxBlockBaseSize: 1_000_000,
+ maxStandardVersion: 2,
+ maxStandardTxWeight: 150_000,
+ maxBlockSigopsCost: 20_000,
+ maxStandardTxSigopsCost: 20_000 / 2,
+ witnessScaleFactor: 4
+ );
+
+ var buriedDeployments = new BuriedDeploymentsArray
+ {
+ [BuriedDeployments.BIP34] = 0,
+ [BuriedDeployments.BIP65] = 0,
+ [BuriedDeployments.BIP66] = 0
+ };
+
+ // To successfully process the OP_FEDERATION opcode the federations should be known.
+ this.Federations = new Federations();
+
+ // This should mirror the federation registered in CirrusTest.
+ this.Federations.RegisterFederation(new Federation(new[] {
+ new PubKey("021040ef28c82fcffb63028e69081605ed4712910c8384d5115c9ffeacd9dbcae4"),//Node1
+ new PubKey("0244290a31824ba7d53e59c7a29d13dbeca15a9b0d36fdd4d28fce426753107bfc"),//Node2
+ new PubKey("032df4a2d62c0db12cd1d66201819a10788637c9b90a1cd2a5a3f5196fdab7a621"),//Node3
+ new PubKey("028ed190eb4ed6e46440ac6af21d8a67a537bd1bd7edb9cc5177d36d5a0972244d"),//Node4
+ new PubKey("02ff9923324399a188daf4310825a85dd3b89e2301d0ad073295b6f33ae1c72f7a"),//Node5
+ new PubKey("030e03b808ddb51701d4d3dbc0a74a6f9aedfecf23d5f874914641fc81197b239a"),//Node7
+ new PubKey("02270d6c20d3393fad7f74c59d2d26b0824ed016ccbc15e698e7354314459a60a5"),//Node8
+ }));
+
+ this.Consensus = new NBitcoin.Consensus(
+ consensusFactory: consensusFactory,
+ consensusOptions: consensusOptions,
+ coinType: 1, // Per https://github.com/satoshilabs/slips/blob/master/slip-0044.md - testnets share a cointype
+ hashGenesisBlock: genesisBlock.GetHash(),
+ subsidyHalvingInterval: 210000,
+ majorityEnforceBlockUpgrade: 750,
+ majorityRejectBlockOutdated: 950,
+ majorityWindow: 1000,
+ buriedDeployments: buriedDeployments,
+ bip9Deployments: null,
+ bip34Hash: null,
+ minerConfirmationWindow: 2016,
+ maxReorgLength: 500,
+ defaultAssumeValid: null,
+ maxMoney: long.MaxValue,
+ coinbaseMaturity: 50,
+ premineHeight: 2,
+ premineReward: Money.Coins(130000000),
+ proofOfWorkReward: Money.Coins(18),
+ powTargetTimespan: TimeSpan.FromSeconds(14 * 24 * 60 * 60),
+ targetSpacing: TimeSpan.FromSeconds(45),
+ powAllowMinDifficultyBlocks: false,
+ posNoRetargeting: false,
+ powNoRetargeting: false,
+ powLimit: new Target(new uint256("0000ffff00000000000000000000000000000000000000000000000000000000")),
+ minimumChainWork: null,
+ isProofOfStake: true,
+ lastPowBlock: 12500,
+ proofOfStakeLimit: new BigInteger(uint256.Parse("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false)),
+ proofOfStakeLimitV2: new BigInteger(uint256.Parse("000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false)),
+ proofOfStakeReward: Money.Coins(18)
+ );
+
+ this.Consensus.PosEmptyCoinbase = false;
+
+ this.Base58Prefixes = new byte[12][];
+ this.Base58Prefixes[(int)Base58Type.PUBKEY_ADDRESS] = new byte[] { 120 }; // q
+ this.Base58Prefixes[(int)Base58Type.SCRIPT_ADDRESS] = new byte[] { 127 }; // t
+ this.Base58Prefixes[(int)Base58Type.SECRET_KEY] = new byte[] { (120 + 128) };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_NO_EC] = new byte[] { 0x01, 0x42 };
+ this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_EC] = new byte[] { 0x01, 0x43 };
+ this.Base58Prefixes[(int)Base58Type.EXT_PUBLIC_KEY] = new byte[] { (0x04), (0x88), (0xB2), (0x1E) };
+ this.Base58Prefixes[(int)Base58Type.EXT_SECRET_KEY] = new byte[] { (0x04), (0x88), (0xAD), (0xE4) };
+ this.Base58Prefixes[(int)Base58Type.PASSPHRASE_CODE] = new byte[] { 0x2C, 0xE9, 0xB3, 0xE1, 0xFF, 0x39, 0xE2 };
+ this.Base58Prefixes[(int)Base58Type.CONFIRMATION_CODE] = new byte[] { 0x64, 0x3B, 0xF6, 0xA8, 0x9A };
+ this.Base58Prefixes[(int)Base58Type.STEALTH_ADDRESS] = new byte[] { 0x2a };
+ this.Base58Prefixes[(int)Base58Type.ASSET_ID] = new byte[] { 23 };
+ this.Base58Prefixes[(int)Base58Type.COLORED_ADDRESS] = new byte[] { 0x13 };
+
+ this.Bech32Encoders = new Bech32Encoder[2];
+ var encoder = new Bech32Encoder("tstrax");
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_PUBKEY_ADDRESS] = encoder;
+ this.Bech32Encoders[(int)Bech32Type.WITNESS_SCRIPT_ADDRESS] = encoder;
+
+ this.DNSSeeds = new List
+ {
+ new DNSSeedData("testnet1.stratisnetwork.com", "testnet1.stratisnetwork.com")
+ };
+
+ this.SeedNodes = new List
+ {
+ new NetworkAddress(IPAddress.Parse("82.146.153.140"), 27105), // Iain
+ };
+ this.StandardScriptsRegistry = new StraxStandardScriptsRegistry();
+
+ Assert(this.DefaultBanTimeSeconds <= this.Consensus.MaxReorgLength * this.Consensus.TargetSpacing.TotalSeconds / 2);
+
+ Assert(this.Consensus.HashGenesisBlock == uint256.Parse("0x0000db68ff9e74fbaf7654bab4fa702c237318428fa9186055c243ddde6354ca"));
+ Assert(this.Genesis.Header.HashMerkleRoot == uint256.Parse("0xfe6317d42149b091399e7f834ca32fd248f8f26f493c30a35d6eea692fe4fcad"));
+ }
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxTest.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxTest.cs.meta
new file mode 100644
index 0000000..cfcbd79
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/StraxTest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f8d1339dd1e316046965fce6b3457729
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/PoABlockHeader.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/PoABlockHeader.cs
new file mode 100644
index 0000000..b3a784c
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/PoABlockHeader.cs
@@ -0,0 +1,25 @@
+using NBitcoin;
+
+namespace Stratis.Bitcoin.Features.PoA
+{
+#pragma warning disable 618
+ public class PoABlockHeader : BlockHeader
+#pragma warning restore 618
+ {
+ private BlockSignature blockSignature;
+
+ public BlockSignature BlockSignature
+ {
+ get => this.blockSignature;
+ set => this.blockSignature = value;
+ }
+
+ public override void ReadWrite(BitcoinStream stream)
+ {
+ base.ReadWrite(stream);
+
+ // Adding the signature to header because it will be needed for header validation on PoA networks.
+ stream.ReadWrite(ref this.blockSignature);
+ }
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/PoABlockHeader.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/PoABlockHeader.cs.meta
new file mode 100644
index 0000000..4ea76d2
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/PoABlockHeader.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7d62986ceee41bb4b83435f6245458b4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/SmartContractPoABlockHeader.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/SmartContractPoABlockHeader.cs
new file mode 100644
index 0000000..ec8b012
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/SmartContractPoABlockHeader.cs
@@ -0,0 +1,53 @@
+using NBitcoin;
+using Stratis.Bitcoin.Features.PoA;
+using uint256 = NBitcoin.uint256;
+
+namespace Stratis.Bitcoin.Features.SmartContracts.PoA
+{
+ public class SmartContractPoABlockHeader : PoABlockHeader, ISmartContractBlockHeader
+ {
+ ///
+ /// Root of the state trie after execution of this block.
+ ///
+ private uint256 hashStateRoot;
+ public uint256 HashStateRoot { get { return this.hashStateRoot; } set { this.hashStateRoot = value; } }
+
+ ///
+ /// Root of the receipt trie after execution of this block.
+ ///
+ private uint256 receiptRoot;
+ public uint256 ReceiptRoot { get { return this.receiptRoot; } set { this.receiptRoot = value; } }
+
+ ///
+ /// Bitwise-OR of all the blooms generated from all of the smart contract transactions in the block.
+ ///
+ private Bloom logsBloom;
+ public Bloom LogsBloom { get { return this.logsBloom; } set { this.logsBloom = value; } }
+
+ public SmartContractPoABlockHeader() : base()
+ {
+ this.hashStateRoot = 0;
+ this.receiptRoot = 0;
+ this.logsBloom = new Bloom();
+ }
+
+ public override void ReadWrite(BitcoinStream stream)
+ {
+ base.ReadWrite(stream);
+ stream.ReadWrite(ref this.hashStateRoot);
+ stream.ReadWrite(ref this.receiptRoot);
+ stream.ReadWrite(ref this.logsBloom);
+ }
+
+ ///
+ protected override void ReadWriteHashingStream(BitcoinStream stream)
+ {
+ base.ReadWriteHashingStream(stream);
+
+ // All fields included in SC header
+ stream.ReadWrite(ref this.hashStateRoot);
+ stream.ReadWrite(ref this.receiptRoot);
+ stream.ReadWrite(ref this.logsBloom);
+ }
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/SmartContractPoABlockHeader.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/SmartContractPoABlockHeader.cs.meta
new file mode 100644
index 0000000..ae5c9d4
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/SmartContractPoABlockHeader.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 05fd2b4927247194cb228196bfe7564e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/StraxNetwork.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/StraxNetwork.cs
new file mode 100644
index 0000000..8422fed
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/StraxNetwork.cs
@@ -0,0 +1,69 @@
+using NBitcoin;
+using NBitcoin.DataEncoders;
+
+namespace Stratis.Bitcoin.Networks
+{
+ public static class StraxNetwork
+ {
+ /// Stratis maximal value for the calculated time offset. If the value is over this limit, the time syncing feature will be switched off.
+ public const int StratisMaxTimeOffsetSeconds = 25 * 60;
+
+ /// Stratis default value for the maximum tip age in seconds to consider the node in initial block download (2 hours).
+ public const int StratisDefaultMaxTipAgeInSeconds = 2 * 60 * 60;
+
+ /// The name of the root folder containing the different Stratis blockchains (StratisMain, StratisTest, StratisRegTest).
+ public const string StraxRootFolderName = "strax";
+
+ /// The default name used for the Strax configuration file.
+ public const string StraxDefaultConfigFilename = "strax.conf";
+
+ public static Block CreateGenesisBlock(ConsensusFactory consensusFactory, uint time, uint nonce, uint bits, int version, Money genesisReward, string genesisText)
+ {
+ Transaction txNew = consensusFactory.CreateTransaction();
+ txNew.Version = 1;
+ txNew.AddInput(new TxIn()
+ {
+ ScriptSig = new Script(Op.GetPushOp(0), new Op()
+ {
+ Code = (OpcodeType)0x1,
+ PushData = new[] { (byte)42 }
+ }, Op.GetPushOp(Encoders.ASCII.DecodeData(genesisText)))
+ });
+ txNew.AddOutput(new TxOut()
+ {
+ Value = genesisReward,
+ });
+
+ Block genesis = consensusFactory.CreateBlock();
+ genesis.Header.BlockTime = Utils.UnixTimeToDateTime(time);
+ genesis.Header.Bits = bits;
+ genesis.Header.Nonce = nonce;
+ genesis.Header.Version = version;
+ genesis.Transactions.Add(txNew);
+ genesis.Header.HashPrevBlock = uint256.Zero;
+ genesis.UpdateMerkleRoot();
+
+ /*
+ Procedure for creating a new genesis block:
+ 1. Create the template block as above in the CreateStraxGenesisBlock method
+
+ 3. Iterate over the nonce until the proof-of-work is valid
+ */
+
+ //while (!genesis.CheckProofOfWork())
+ //{
+ // genesis.Header.Nonce++;
+ // if (genesis.Header.Nonce == 0)
+ // genesis.Header.Time++;
+ //}
+
+ /*
+ 4. This will mean the block header hash is under the target
+ 5. Retrieve the Nonce and Time values from the resulting block header and insert them into the network definition
+ */
+
+ return genesis;
+
+ }
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/StraxNetwork.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/StraxNetwork.cs.meta
new file mode 100644
index 0000000..d01d993
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Dependencies/StraxNetwork.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 204e609f3d29802459588868c0b1e6ce
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Examples.meta b/Src/StratisUnity3d/Assets/Code/Examples.meta
new file mode 100644
index 0000000..eef49a1
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Examples.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 570e5e4759174b24fb691b0c51405130
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Examples/TestApiMethods.cs b/Src/StratisUnity3d/Assets/Code/Examples/TestApiMethods.cs
new file mode 100644
index 0000000..5836d12
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Examples/TestApiMethods.cs
@@ -0,0 +1,52 @@
+using UnityEngine;
+using NBitcoin;
+using Stratis.Bitcoin.Networks;
+using Unity3dApi;
+using Network = NBitcoin.Network;
+using ValidatedAddress = Unity3dApi.ValidatedAddress;
+
+public class TestApiMethods : MonoBehaviour
+{
+ async void Start()
+ {
+ // Create network instance. Strax test and main & cirrus test and main are supported.
+ Network network = new StraxMain();
+
+ // API client used to interact with strax node. Note that you should run node with '-txindex=1 -addressindex=1 -unityapi_enable=true' arguments.
+ Unity3dClient client = new Unity3dClient("http://localhost:44336/");
+
+ //Mnemonic newMnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve);
+ Mnemonic mnemonic = new Mnemonic("leopard fire legal door attract stove similar response photo prize seminar frown", Wordlist.English);
+ StratisUnityManager stratisUnityManager = new StratisUnityManager(client, network, mnemonic);
+
+ Debug.Log("Your address: " + stratisUnityManager.GetAddress());
+
+ decimal balance = await stratisUnityManager.GetBalanceAsync();
+ Debug.Log("Your balance: " + balance);
+
+ //await stratisUnityManager.SendTransactionAsync("XRfFRF7M3xnAu6FutrdqmgBqJFQ16ZKRW2", (long)(0.1 * Money.COIN));
+ //await stratisUnityManager.SendOpReturnTransactionAsync("WAZAAAA");
+
+ // Provides list of UTXOs for target address.
+ GetUTXOsResponseModel utxos = await client.GetUTXOsForAddressAsync("XUQUbLJ9TMFXsvKbfpitvbBnMPzJwM2ftQ");
+
+ // Provides block header for given block hash.
+ BlockHeaderModel blockHeader = await client.GetBlockHeaderAsync("d4a1678831917514bbae45354ff9cf4638a495558bffc66c9fe07313cce810d7");
+
+ // Gets tx hex by tx id.
+ // This method also can be used to check if tx that was sent was actually included in a block.
+ RawTxModel rawTx = await client.GetRawTransactionAsync("b3de5b8d0bff178a0aeff695a7959fbc0362adf82b2aec8ad02c61b41ffa5cab");
+ Transaction decodedTx = network.CreateTransaction(rawTx.Hex);
+
+ //Validates given address.
+ bool isValid = (await client.ValidateAddressAsync("XPn7rte7zHuH2PnrK3WRZqa3Stpb8YLvq6")).Isvalid;
+
+ // Provides block by given block hash.
+ BlockModel block = await client.BlockAsync("1726b2a83a92dfa932fca44f1a8f8ac430346aa3b9dc1a4bef2988676efe7b31", true, true);
+
+ // Provides tip (hash and height of the last synced block).
+ TipModel tip = await client.TipAsync();
+
+ Debug.Log("Api test done");
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Examples/TestApiMethods.cs.meta b/Src/StratisUnity3d/Assets/Code/Examples/TestApiMethods.cs.meta
new file mode 100644
index 0000000..ade1431
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Examples/TestApiMethods.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 75f4330360b226548aae7fbd166b29dd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Examples/TestUI.cs b/Src/StratisUnity3d/Assets/Code/Examples/TestUI.cs
new file mode 100644
index 0000000..18b2a95
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Examples/TestUI.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using NBitcoin;
+using Stratis.Bitcoin.Networks;
+using Unity3dApi;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class TestUI : MonoBehaviour
+{
+ public Button TestAPI_Button, GenerateMnemonic_Button, InitializeApi_Button, CopyAddress_Button, RefreshBalance_Button, SendTx_Button, SendOpReturnTx_Button;
+
+ public InputField ApiUrl_InputField, Mnemonic_InputField, DestinationAddress_InputField, Amount_InputField, OpReturnData_InputField;
+
+ public Text Address_Text, Balance_Text;
+
+ public GameObject PopupPanel;
+
+ public Text PopupPanel_Text;
+
+ public Button PopupPanelOk_Button;
+
+ private StratisUnityManager stratisUnityManager;
+
+ void Start()
+ {
+ this.ApiUrl_InputField.text = "http://localhost:44336/";
+ this.Mnemonic_InputField.text = "leopard fire legal door attract stove similar response photo prize seminar frown";
+
+ PopupPanel.SetActive(false);
+
+ TestAPI_Button.onClick.AddListener(() => this.StartCoroutine(TestApi_ButtonCall()));
+ GenerateMnemonic_Button.onClick.AddListener(GenerateMnemonic_ButtonCall);
+ InitializeApi_Button.onClick.AddListener(InitializeApi_ButtonCall);
+ CopyAddress_Button.onClick.AddListener(CopyAddress_ButtonCall);
+ RefreshBalance_Button.onClick.AddListener(RefreshBalance_ButtonCall);
+ SendTx_Button.onClick.AddListener(() => this.StartCoroutine(SendTx_ButtonCall()));
+ SendOpReturnTx_Button.onClick.AddListener(() => this.StartCoroutine(SendOpReturnTx_ButtonCall()));
+
+ PopupPanelOk_Button.onClick.AddListener(() => PopupPanel.SetActive(false));
+ }
+
+ private IEnumerator TestApi_ButtonCall()
+ {
+ Unity3dClient client = new Unity3dClient(ApiUrl_InputField.text);
+
+ string resultString = "";
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ await client.TipAsync().ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ resultString = "Api error. Check that API URL is correct and can be reached. Exception: " + e.ToString();
+ return;
+ }
+
+ resultString = "API test successful.";
+ });
+
+ while (!task.IsCompleted)
+ yield return null;
+
+ this.DisplayPopup(resultString);
+ }
+
+ private void GenerateMnemonic_ButtonCall()
+ {
+ Mnemonic mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve);
+ this.Mnemonic_InputField.text = mnemonic.ToString();
+ }
+
+ private void InitializeApi_ButtonCall()
+ {
+ try
+ {
+ this.stratisUnityManager = new StratisUnityManager(new Unity3dClient(ApiUrl_InputField.text), new StraxMain(), new Mnemonic(this.Mnemonic_InputField.text, Wordlist.English));
+
+ this.DisplayPopup("StratisUnityManager initialized.");
+
+ this.Address_Text.text = this.stratisUnityManager.GetAddress().ToString();
+ this.StartCoroutine(RefreshBalance());
+ }
+ catch (Exception e)
+ {
+ this.DisplayPopup(e.ToString());
+ }
+ }
+
+ private IEnumerator RefreshBalance()
+ {
+ decimal balance = -1;
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ balance = await this.stratisUnityManager.GetBalanceAsync();
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e.ToString());
+ return;
+ }
+ });
+
+ while (!task.IsCompleted)
+ yield return null;
+
+ this.Balance_Text.text = balance.ToString();
+ }
+
+ private void CopyAddress_ButtonCall()
+ {
+ GUIUtility.systemCopyBuffer = this.Address_Text.text;
+ }
+
+ private void RefreshBalance_ButtonCall()
+ {
+ this.StartCoroutine(RefreshBalance());
+ }
+
+ private IEnumerator SendTx_ButtonCall()
+ {
+ string destAddress = this.DestinationAddress_InputField.text;
+ Money amount = new Money(Decimal.Parse(this.Amount_InputField.text), MoneyUnit.BTC);
+
+ string txHash = null;
+ string error = null;
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ txHash = await this.stratisUnityManager.SendTransactionAsync(destAddress, amount);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e.ToString());
+ error = e.ToString();
+ return;
+ }
+ });
+
+ while (!task.IsCompleted)
+ yield return null;
+
+ if (error != null)
+ {
+ this.DisplayPopup("Error sending tx: " + error);
+ }
+ else
+ {
+ this.DisplayPopup(string.Format("Transaction {0} to {1} with amount: {2} was sent.", txHash, destAddress, amount));
+ }
+
+ this.DestinationAddress_InputField.text = "";
+ this.Amount_InputField.text = "";
+ }
+
+ private IEnumerator SendOpReturnTx_ButtonCall()
+ {
+ string opReturnData = this.OpReturnData_InputField.text;
+ string txHash = null;
+ string error = null;
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ txHash = await this.stratisUnityManager.SendOpReturnTransactionAsync(opReturnData);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e.ToString());
+ error = e.ToString();
+ return;
+ }
+ });
+
+ while (!task.IsCompleted)
+ yield return null;
+
+ if (error != null)
+ {
+ this.DisplayPopup("Error sending tx: " + error);
+ }
+ else
+ {
+ this.DisplayPopup(string.Format("Transaction {0} with op_return data {1} was sent.", txHash, opReturnData));
+ }
+
+ this.OpReturnData_InputField.text = "";
+ }
+
+ private void DisplayPopup(string text)
+ {
+ SynchronizationContext.Current.Post(state =>
+ {
+ this.PopupPanel.SetActive(true);
+ this.PopupPanel_Text.text = text;
+ }, null);
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/Examples/TestUI.cs.meta b/Src/StratisUnity3d/Assets/Code/Examples/TestUI.cs.meta
new file mode 100644
index 0000000..cc91e25
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Examples/TestUI.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ed3cf3c2a4a15c34ebd36115d44b1d48
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs b/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs
new file mode 100644
index 0000000..16f768c
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs
@@ -0,0 +1,115 @@
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NBitcoin;
+using Unity3dApi;
+using UnityEngine;
+using Network = NBitcoin.Network;
+
+public class StratisUnityManager
+{
+ private Unity3dClient client;
+
+ private Network network;
+
+ private Mnemonic mnemonic;
+
+ private Key privateKey;
+
+ private PubKey publicKey;
+
+ private BitcoinPubKeyAddress address;
+
+ public StratisUnityManager(Unity3dClient client, Network network, Mnemonic mnemonic)
+ {
+ this.client = client;
+ this.network = network;
+ this.mnemonic = mnemonic;
+
+ this.privateKey = this.mnemonic.DeriveExtKey().PrivateKey;
+ this.publicKey = this.privateKey.PubKey;
+ this.address = this.publicKey.GetAddress(network);
+ }
+
+ public async Task GetBalanceAsync()
+ {
+ long balanceSat = await client.GetAddressBalanceAsync(this.address.ToString()).ConfigureAwait(false);
+
+ decimal balance = new Money(balanceSat).ToUnit(MoneyUnit.BTC);
+
+ return balance;
+ }
+
+ public BitcoinPubKeyAddress GetAddress()
+ {
+ return this.address;
+ }
+
+ public async Task SendTransactionAsync(string destinationAddress, Money sendAmount)
+ {
+ Coin[] coins = await this.GetCoinsAsync().ConfigureAwait(false);
+
+ BitcoinPubKeyAddress addrTo = new BitcoinPubKeyAddress(destinationAddress, this.network);
+
+ var txBuilder = new TransactionBuilder(this.network);
+ Transaction tx = txBuilder
+ .AddCoins(coins)
+ .AddKeys(this.privateKey)
+ .Send(addrTo, sendAmount)
+ .SendFees("0.0001")
+ .SetChange(this.address)
+ .BuildTransaction(true);
+
+ if (!txBuilder.Verify(tx))
+ Debug.LogError("Tx wasn't fully signed!");
+
+ Debug.Log(string.Format("Created tx {0} to {1}, amount: {2}.", tx.GetHash(), destinationAddress, sendAmount));
+
+ await client.SendTransactionAsync(new SendTransactionRequest() {Hex = tx.ToHex()}).ConfigureAwait(false);
+
+ Debug.Log("Transaction sent.");
+ return tx.GetHash().ToString();
+ }
+
+ public async Task SendOpReturnTransactionAsync(string opReturnData)
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(opReturnData);
+
+ return await this.SendOpReturnTransactionAsync(bytes).ConfigureAwait(false);
+ }
+
+ public async Task SendOpReturnTransactionAsync(byte[] bytes)
+ {
+ Coin[] coins = await this.GetCoinsAsync().ConfigureAwait(false);
+
+ Script opReturnScript = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes);
+
+ var txBuilder = new TransactionBuilder(this.network);
+ Transaction tx = txBuilder
+ .AddCoins(coins)
+ .AddKeys(this.privateKey)
+ .Send(opReturnScript, Money.Zero)
+ .SendFees("0.0001")
+ .SetChange(this.address)
+ .BuildTransaction(true);
+
+ if (!txBuilder.Verify(tx))
+ Debug.LogError("Tx wasn't fully signed!");
+
+ Debug.Log(string.Format("Created OP_RETURN tx {0}, data: {1}.", tx.GetHash(), Encoding.UTF8.GetString(bytes)));
+
+ await client.SendTransactionAsync(new SendTransactionRequest() { Hex = tx.ToHex() }).ConfigureAwait(false);
+
+ Debug.Log("Transaction sent.");
+ return tx.GetHash().ToString();
+ }
+
+ private async Task GetCoinsAsync()
+ {
+ GetUTXOsResponseModel utxos = await client.GetUTXOsForAddressAsync(this.address.ToString()).ConfigureAwait(false);
+
+ Coin[] coins = utxos.Utxos.Select(x => new Coin(new OutPoint(uint256.Parse(x.Hash), x.N), new TxOut(new Money(x.Satoshis), address))).ToArray();
+
+ return coins;
+ }
+}
diff --git a/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs.meta b/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs.meta
new file mode 100644
index 0000000..1d4addd
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e8e2ece2391786245950fc6c36e0fbaf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/StratisUnity3d/Assets/Code/Unity3dApi.cs b/Src/StratisUnity3d/Assets/Code/Unity3dApi.cs
new file mode 100644
index 0000000..9ee28b7
--- /dev/null
+++ b/Src/StratisUnity3d/Assets/Code/Unity3dApi.cs
@@ -0,0 +1,1092 @@
+//----------------------
+//
+// Generated using the NSwag toolchain v13.11.1.0 (NJsonSchema v10.4.3.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
+//
+//----------------------
+
+#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
+#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
+#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
+#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
+#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
+#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
+
+namespace Unity3dApi
+{
+ using System = global::System;
+
+ [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.1.0 (NJsonSchema v10.4.3.0 (Newtonsoft.Json v12.0.0.0))")]
+ public partial class Unity3dClient
+ {
+ private string _baseUrl = "";
+ private System.Lazy _settings;
+
+ public Unity3dClient(string baseUrl)
+ {
+ BaseUrl = baseUrl;
+ _settings = new System.Lazy(CreateSerializerSettings);
+ }
+
+ private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
+ {
+ var settings = new Newtonsoft.Json.JsonSerializerSettings();
+ UpdateJsonSerializerSettings(settings);
+ return settings;
+ }
+
+ public string BaseUrl
+ {
+ get { return _baseUrl; }
+ set { _baseUrl = value; }
+ }
+
+ protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
+
+ partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
+
+
+ partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
+ partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
+ partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task GetUTXOsForAddressAsync(string address)
+ {
+ return GetUTXOsForAddressAsync(address, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task GetUTXOsForAddressAsync(string address, System.Threading.CancellationToken cancellationToken)
+ {
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/getutxosforaddress?");
+ if (address != null)
+ {
+ urlBuilder_.Append(System.Uri.EscapeDataString("address") + "=").Append(System.Uri.EscapeDataString(ConvertToString(address, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ }
+ urlBuilder_.Length--;
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ request_.Method = new System.Net.Http.HttpMethod("GET");
+ request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ return objectResponse_.Object;
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task GetAddressBalanceAsync(string address)
+ {
+ return GetAddressBalanceAsync(address, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task GetAddressBalanceAsync(string address, System.Threading.CancellationToken cancellationToken)
+ {
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/getaddressbalance?");
+ if (address != null)
+ {
+ urlBuilder_.Append(System.Uri.EscapeDataString("address") + "=").Append(System.Uri.EscapeDataString(ConvertToString(address, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ }
+ urlBuilder_.Length--;
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ request_.Method = new System.Net.Http.HttpMethod("GET");
+ request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ return objectResponse_.Object;
+ }
+ else
+ if (status_ == 400)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task GetBlockHeaderAsync(string hash)
+ {
+ return GetBlockHeaderAsync(hash, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task GetBlockHeaderAsync(string hash, System.Threading.CancellationToken cancellationToken)
+ {
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/getblockheader?");
+ if (hash != null)
+ {
+ urlBuilder_.Append(System.Uri.EscapeDataString("hash") + "=").Append(System.Uri.EscapeDataString(ConvertToString(hash, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ }
+ urlBuilder_.Length--;
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ request_.Method = new System.Net.Http.HttpMethod("GET");
+ request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ return objectResponse_.Object;
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task GetRawTransactionAsync(string trxid)
+ {
+ return GetRawTransactionAsync(trxid, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task GetRawTransactionAsync(string trxid, System.Threading.CancellationToken cancellationToken)
+ {
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/getrawtransaction?");
+ if (trxid != null)
+ {
+ urlBuilder_.Append(System.Uri.EscapeDataString("trxid") + "=").Append(System.Uri.EscapeDataString(ConvertToString(trxid, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ }
+ urlBuilder_.Length--;
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ request_.Method = new System.Net.Http.HttpMethod("GET");
+ request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ return objectResponse_.Object;
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task SendTransactionAsync(SendTransactionRequest body)
+ {
+ return SendTransactionAsync(body, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task SendTransactionAsync(SendTransactionRequest body, System.Threading.CancellationToken cancellationToken)
+ {
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/send-transaction");
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
+ content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json-patch+json");
+ request_.Content = content_;
+ request_.Method = new System.Net.Http.HttpMethod("POST");
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ return;
+ }
+ else
+ if (status_ == 400)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
+ }
+ else
+ if (status_ == 403)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
+ }
+ else
+ if (status_ == 500)
+ {
+ string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("Server Error", status_, responseText_, headers_, null);
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task ValidateAddressAsync(string address)
+ {
+ return ValidateAddressAsync(address, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task ValidateAddressAsync(string address, System.Threading.CancellationToken cancellationToken)
+ {
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/validateaddress?");
+ if (address != null)
+ {
+ urlBuilder_.Append(System.Uri.EscapeDataString("address") + "=").Append(System.Uri.EscapeDataString(ConvertToString(address, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ }
+ urlBuilder_.Length--;
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ request_.Method = new System.Net.Http.HttpMethod("GET");
+ request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ return objectResponse_.Object;
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task BlockAsync(string hash, bool? showTransactionDetails, bool? outputJson)
+ {
+ return BlockAsync(hash, showTransactionDetails, outputJson, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task BlockAsync(string hash, bool? showTransactionDetails, bool? outputJson, System.Threading.CancellationToken cancellationToken)
+ {
+ if (hash == null)
+ throw new System.ArgumentNullException("hash");
+
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/block?");
+ urlBuilder_.Append(System.Uri.EscapeDataString("Hash") + "=").Append(System.Uri.EscapeDataString(ConvertToString(hash, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ if (showTransactionDetails != null)
+ {
+ urlBuilder_.Append(System.Uri.EscapeDataString("ShowTransactionDetails") + "=").Append(System.Uri.EscapeDataString(ConvertToString(showTransactionDetails, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ }
+ if (outputJson != null)
+ {
+ urlBuilder_.Append(System.Uri.EscapeDataString("OutputJson") + "=").Append(System.Uri.EscapeDataString(ConvertToString(outputJson, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
+ }
+ urlBuilder_.Length--;
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ request_.Method = new System.Net.Http.HttpMethod("GET");
+ request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ return objectResponse_.Object;
+ }
+ else
+ if (status_ == 400)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+ /// Success
+ /// A server side error occurred.
+ public System.Threading.Tasks.Task TipAsync()
+ {
+ return TipAsync(System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ public async System.Threading.Tasks.Task TipAsync(System.Threading.CancellationToken cancellationToken)
+ {
+ var urlBuilder_ = new System.Text.StringBuilder();
+ urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/tip");
+
+ var client_ = new System.Net.Http.HttpClient();
+ var disposeClient_ = false;
+ try
+ {
+ using (var request_ = new System.Net.Http.HttpRequestMessage())
+ {
+ request_.Method = new System.Net.Http.HttpMethod("GET");
+ request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
+
+ PrepareRequest(client_, request_, urlBuilder_);
+
+ var url_ = urlBuilder_.ToString();
+ request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
+
+ PrepareRequest(client_, request_, url_);
+
+ var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ var disposeResponse_ = true;
+ try
+ {
+ var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+ if (response_.Content != null && response_.Content.Headers != null)
+ {
+ foreach (var item_ in response_.Content.Headers)
+ headers_[item_.Key] = item_.Value;
+ }
+
+ ProcessResponse(client_, response_);
+
+ var status_ = (int)response_.StatusCode;
+ if (status_ == 200)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ return objectResponse_.Object;
+ }
+ else
+ if (status_ == 400)
+ {
+ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
+ if (objectResponse_.Object == null)
+ {
+ throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
+ }
+ throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
+ }
+ else
+ {
+ var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
+ throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
+ }
+ }
+ finally
+ {
+ if (disposeResponse_)
+ response_.Dispose();
+ }
+ }
+ }
+ finally
+ {
+ if (disposeClient_)
+ client_.Dispose();
+ }
+ }
+
+ protected struct ObjectResponseResult
+ {
+ public ObjectResponseResult(T responseObject, string responseText)
+ {
+ this.Object = responseObject;
+ this.Text = responseText;
+ }
+
+ public T Object { get; }
+
+ public string Text { get; }
+ }
+
+ public bool ReadResponseAsString { get; set; }
+
+ protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken)
+ {
+ if (response == null || response.Content == null)
+ {
+ return new ObjectResponseResult(default(T), string.Empty);
+ }
+
+ if (ReadResponseAsString)
+ {
+ var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ try
+ {
+ var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings);
+ return new ObjectResponseResult(typedBody, responseText);
+ }
+ catch (Newtonsoft.Json.JsonException exception)
+ {
+ var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
+ throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
+ }
+ }
+ else
+ {
+ try
+ {
+ using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
+ using (var streamReader = new System.IO.StreamReader(responseStream))
+ using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
+ {
+ var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
+ var typedBody = serializer.Deserialize(jsonTextReader);
+ return new ObjectResponseResult(typedBody, string.Empty);
+ }
+ }
+ catch (Newtonsoft.Json.JsonException exception)
+ {
+ var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
+ throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
+ }
+ }
+ }
+
+ private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
+ {
+ if (value == null)
+ {
+ return "";
+ }
+
+ if (value is System.Enum)
+ {
+ var name = System.Enum.GetName(value.GetType(), value);
+ if (name != null)
+ {
+ var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
+ if (field != null)
+ {
+ var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
+ as System.Runtime.Serialization.EnumMemberAttribute;
+ if (attribute != null)
+ {
+ return attribute.Value != null ? attribute.Value : name;
+ }
+ }
+
+ var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
+ return converted == null ? string.Empty : converted;
+ }
+ }
+ else if (value is bool)
+ {
+ return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
+ }
+ else if (value is byte[])
+ {
+ return System.Convert.ToBase64String((byte[]) value);
+ }
+ else if (value.GetType().IsArray)
+ {
+ var array = System.Linq.Enumerable.OfType