diff --git a/README.md b/README.md index fff2727..cc76d8e 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,14 @@ Unity3d client for Stratis. + + + +Video manual on how to setup and use basic light wallet functionality: https://youtu.be/hNpdUokMQlE + + + +Smart contract usage example can be found in TestSmartContracts.cs file. + +More in depth information on how to create and interact with smart contracts can be found here: https://academy.stratisplatform.com/Operation%20Guides/SmartContracts/smartcontracts-introduction.html + diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs index 12623ec..d91d036 100644 --- a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusMain.cs @@ -5,7 +5,7 @@ namespace Stratis.Sidechains.Networks { - public class CirrusMain : Network + public class CirrusMain : PoANetwork { public CirrusMain() { @@ -44,6 +44,7 @@ public CirrusMain() this.Genesis = genesisBlock; + this.Federations = new Federations(); var straxFederationTransactionSigningKeys = new List() { @@ -66,6 +67,29 @@ public CirrusMain() // Register the new set of federation members. this.Federations.RegisterFederation(new Federation(straxFederationTransactionSigningKeys)); + + // The height at which the following list of members apply. + this.MultisigMinersApplicabilityHeight = 1413998; + + // Set the list of Strax Era mining keys. + this.StraxMiningMultisigMembers = new List() + { + new PubKey("02ace4fbe6a622cdfc922a447c3253e8635f3fecb69241f73629e6f0596a567907"), + new PubKey("028e1d9fd64b84a2ec85fac7185deb2c87cc0dd97270cf2d8adc3aa766dde975a7"), + new PubKey("025cb67811d0922ca77fa33f19c3e5c37961f9639a1f0a116011b9075f6796abcb"), + new PubKey("027e793fbf4f6d07de15b0aa8355f88759b8bdf92a9ffb8a65a87fa8ee03baeccd"), + new PubKey("03eb5db0b1703ea7418f0ad20582bf8de0b4105887d232c7724f43f19f14862488"), + new PubKey("03e8809be396745434ee8c875089e518a3eef40e31ade81869ce9cbef63484996d"), + new PubKey("0317abe6a28cc7af44a46de97e7c6120c1ccec78afb83efe18030f5c36e3016b32"), + new PubKey("038e1a76f0e33474144b61e0796404821a5150c00b05aad8a1cd502c865d8b5b92"), + new PubKey("036437789fac0ab74cda93d98b519c28608a48ef86c3bd5e8227af606c1e025f61"), + new PubKey("03d8b5580b7ec709c006ef497327db27ea323bd358ca45412171c644214483b74f"), + new PubKey("02f40bd4f662ba20629a104115f0ac9ee5eab695716edfe01b240abf56e05797e2"), + new PubKey("0323033679aa439a0388f09f2883bf1ca6f50283b41bfeb6be6ddcc4e420144c16"), + new PubKey("03535a285d0919a9bd71df3b274cecb46e16b78bf50d3bf8b0a3b41028cf8a842d"), + new PubKey("03a37019d2e010b046ef9d0459e4844a015758007602ddfbdc9702534924a23695"), + new PubKey("03f5de5176e29e1e7d518ae76c1e020b1da18b57a3713ac81b16015026e232748e"), + }; var buriedDeployments = new BuriedDeploymentsArray { @@ -75,41 +99,7 @@ public CirrusMain() }; 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 @@ -130,11 +120,34 @@ public CirrusMain() 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")); + this.Checkpoints = new Dictionary() + { + { 50000, new CheckpointInfo(new uint256("0xf3ed37db1c56751fdf9f45902696dd034444a697cd8c106a08f4c60cd2de9d77")) }, + { 100000, new CheckpointInfo(new uint256("0x1400cb20800d54cd7fff5fea90133a1a8ca44e7043268cd0c7efdd7f8186b2d0")) }, + { 150000, new CheckpointInfo(new uint256("0x505d22805f0fc4ea057edad778e7334412526a7c1b017b179be5d274c8d42914")) }, + { 200000, new CheckpointInfo(new uint256("0x5569221c600e42b0467c92bd932046c12198eee5c50ac98eadff7d3159f55b75")) }, + { 250000, new CheckpointInfo(new uint256("0x1a0d5f43335eff00e8a3b5dc09e4f6849b571b6870eb58364cf86623222922d7")) }, + { 300000, new CheckpointInfo(new uint256("0x3b1c3704e0cb79e7fff46ab7e9feacbfa9e2e95ab90b273d99520dbd42cc34b6")) }, + { 350000, new CheckpointInfo(new uint256("0xcb420b8ef20e1da9eb63b6847005b17928b4bad6c2920eebc964ecf21c50ce5a")) }, + { 400000, new CheckpointInfo(new uint256("0xa501a5c69dfce78e39bf0c25d2c1eafa9fd7a9f32ee06b419d3a3c0a6ac29d8b")) }, + { 450000, new CheckpointInfo(new uint256("0xc3ae6119d23294ac51c05f9c761da5271711b1945592cb83cc1bcc1b908780c7")) }, + { 500000, new CheckpointInfo(new uint256("0x810cc011d6d5158aaefcc38550a31b4118fae1bb18ea7894f81a2edc81126d5f")) }, + { 550000, new CheckpointInfo(new uint256("0x3a6b0a58deb1997879d35fc6e017123594c00eafb3ac45d8c31a5dbf68c2bccc")) }, + { 600000, new CheckpointInfo(new uint256("0xc79bf7066ec9243a335fcd2a43380a47a5b9dccdeaee3f67ab5503cef0cd1626")) }, + { 700000, new CheckpointInfo(new uint256("0xe777ae5e283564a994cbcf88315a594854c12d626e6908fb27e3d0cd7d04fcc7")) }, + { 800000, new CheckpointInfo(new uint256("0xe8b2b9b4e342b0ff9a0b1b967b0f2b7481fe420c5922322d1b77cfae66471fa1")) }, + { 900000, new CheckpointInfo(new uint256("0x30599fbbce4404ebaff9f8d0ea7071c684f124439f1f4e9fabec0debad6c7a06")) }, + { 1_000_000, new CheckpointInfo(new uint256("0x547faf99acb45e2195ea5fbb6873562c44a7696f6571e8a309d6c9f509be064a")) }, + { 1_100_000, new CheckpointInfo(new uint256("0x7abc2882bcb5e9723ba71ff4155ed3c4006ee655e9f52f8787bcae31b4c796a8")) }, + { 1_200_000, new CheckpointInfo(new uint256("0x8411b830270cc9d6c2e28de1c2e8025c57a5673835f63e30708967adfee5a92c")) }, + { 1_300_000, new CheckpointInfo(new uint256("0x512c19a8245316b4d3b13513c7901f41842846f539f668ca4ac349daaab6dc20")) }, + { 1_400_000, new CheckpointInfo(new uint256("0xbfd4a96a6c5250f18bf7c586761256fa5f8753ffa10b24160f0648a452823a95")) }, + { 1_500_000, new CheckpointInfo(new uint256("0x2a1602877a5231997654bae975223762ee636be2f371cb444b2d3fb564e6989e")) }, + { 1_750_000, new CheckpointInfo(new uint256("0x58c96a878efeeffea1b1924b61eed627687900e01588ffaa2f4a161973f01abf")) }, + { 1_850_000, new CheckpointInfo(new uint256("0x6e2590bd9a8eaab25b236c0c9ac314abec70b18aa053b96c9257f2356dec8314")) }, + { 2_150_000, new CheckpointInfo(new uint256("0x4c65f29b5098479cab275afd77d302ebe5ed8d8ef33e02ae54bf185865763f18")) }, + { 2_500_000, new CheckpointInfo(new uint256("0x2853be7b7224840d3d4b60427ea832e9bd67d8fc6bfcd4956b8c6b2414cf8fc2")) }, + }; } } diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs index a2dcba0..6e394eb 100644 --- a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/CirrusTest.cs @@ -158,11 +158,6 @@ public CirrusTest() 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/PoANetwork.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/PoANetwork.cs new file mode 100644 index 0000000..cb84016 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/PoANetwork.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using NBitcoin; +using NBitcoin.DataEncoders; +using NBitcoin.Protocol; +using Stratis.Sidechains.Networks; + +public class PoANetwork : Network +{ + /// The name of the root folder containing the different PoA blockchains. + private const string NetworkRootFolderName = "poa"; + + /// The default name used for the Stratis configuration file. + private const string NetworkDefaultConfigFilename = "poa.conf"; + + /// + /// This is the height at which collateral commitment height data was committed to blocks. + /// + public int CollateralCommitmentActivationHeight { get; set; } + + /// + /// The height at which the StraxMiningMultisigMembers become applicable. + /// + public int? MultisigMinersApplicabilityHeight { get; set; } + + /// The mining keys of the new multisig members to become active with the first Strax-era Cirrus collateral block mined. + public IList StraxMiningMultisigMembers { get; protected set; } + + public IList FederationKeys { get; set; } + + public PoANetwork() + { + // The message start string is designed to be unlikely to occur in normal data. + // The characters are rarely used upper ASCII, not valid as UTF-8, and produce + // a large 4-byte int at any alignment. + var messageStart = new byte[4]; + messageStart[0] = 0x76; + messageStart[1] = 0x36; + messageStart[2] = 0x23; + messageStart[3] = 0x06; + uint magic = BitConverter.ToUInt32(messageStart, 0); + + this.Name = "PoAMain"; + this.NetworkType = NetworkType.Mainnet; + this.Magic = magic; + this.DefaultPort = 16438; + this.DefaultMaxOutboundConnections = 16; + this.DefaultMaxInboundConnections = 109; + this.DefaultRPCPort = 16474; + this.DefaultAPIPort = 37221; // TODO: Confirm + this.MaxTipAge = 2 * 60 * 60; + this.MinTxFee = 10000; + this.FallbackFee = 10000; + this.MinRelayTxFee = 10000; + this.RootFolderName = NetworkRootFolderName; + this.DefaultConfigFilename = NetworkDefaultConfigFilename; + this.MaxTimeOffsetSeconds = 25 * 60; + this.CoinTicker = "POA"; + + // Create the genesis block. + this.GenesisTime = 1513622125; + this.GenesisNonce = 1560058197; + this.GenesisBits = 402691653; + this.GenesisVersion = 1; + this.GenesisReward = Money.Zero; + + Block genesisBlock = CreatePoAGenesisBlock(new ConsensusFactory(), this.GenesisTime, this.GenesisNonce, this.GenesisBits, this.GenesisVersion, this.GenesisReward); + + this.Genesis = genesisBlock; + + var buriedDeployments = new BuriedDeploymentsArray + { + [BuriedDeployments.BIP34] = 0, + [BuriedDeployments.BIP65] = 0, + [BuriedDeployments.BIP66] = 0 + }; + + var bip9Deployments = new NoBIP9Deployments(); + + var consensusOptions = new ConsensusOptions( + maxBlockBaseSize: 1_000_000, + maxStandardVersion: 2, + maxStandardTxWeight: 150_000, + maxBlockSigopsCost: 20_000, + maxStandardTxSigopsCost: 20_000 / 5, 1 + ) + { + EnforceMinProtocolVersionAtBlockHeight = 384675, // setting the value to zero makes the functionality inactive + EnforcedMinProtocolVersion = ProtocolVersion.CIRRUS_VERSION, // minimum protocol version which will be enforced at block height defined in EnforceMinProtocolVersionAtBlockHeight + }; + + this.Consensus = new Consensus( + consensusFactory: new ConsensusFactory(), + consensusOptions: consensusOptions, + 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 + ); + + // https://en.bitcoin.it/wiki/List_of_address_prefixes + this.Base58Prefixes = new byte[12][]; + this.Base58Prefixes[(int)Base58Type.PUBKEY_ADDRESS] = new byte[] { (55) }; // 'P' prefix + this.Base58Prefixes[(int)Base58Type.SCRIPT_ADDRESS] = new byte[] { (117) }; // 'p' prefix + this.Base58Prefixes[(int)Base58Type.SECRET_KEY] = new byte[] { (63 + 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.Checkpoints = new Dictionary + { + { 0, new CheckpointInfo(new uint256("0x0621b88fb7a99c985d695be42e606cb913259bace2babe92970547fa033e4076")) }, + }; + + var encoder = new Bech32Encoder("bc"); + 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.Consensus.HashGenesisBlock == uint256.Parse("0x0621b88fb7a99c985d695be42e606cb913259bace2babe92970547fa033e4076")); + Assert(this.Genesis.Header.HashMerkleRoot == uint256.Parse("0x9928b372fd9e4cf62a31638607344c03c48731ba06d24576342db9c8591e1432")); + } + + protected static Block CreatePoAGenesisBlock(ConsensusFactory consensusFactory, uint nTime, uint nNonce, uint nBits, int nVersion, Money genesisReward) + { + string data = "506f41202d204345485450414a6c75334f424148484139205845504839"; + + Transaction txNew = consensusFactory.CreateTransaction(); + txNew.Version = 1; + // TODO: Removing the time field will affect the genesis block hash of the Cirrus networks. Need to make a call about only developing Cirrus via the SBFN project that still has nTime + txNew.AddInput(new TxIn() + { + ScriptSig = new Script(Op.GetPushOp(0), new Op() + { + Code = (OpcodeType)0x1, + PushData = new[] { (byte)42 } + }, Op.GetPushOp(Encoders.ASCII.DecodeData(data))) + }); + txNew.AddOutput(new TxOut() + { + Value = genesisReward, + }); + + Block genesis = consensusFactory.CreateBlock(); + genesis.Header.BlockTime = Utils.UnixTimeToDateTime(nTime); + genesis.Header.Bits = nBits; + genesis.Header.Nonce = nNonce; + genesis.Header.Version = nVersion; + genesis.Transactions.Add(txNew); + genesis.Header.HashPrevBlock = uint256.Zero; + genesis.UpdateMerkleRoot(); + return genesis; + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/PoANetwork.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/PoANetwork.cs.meta new file mode 100644 index 0000000..740ecb7 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/Networks/PoANetwork.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87364372b4503c148b81197a893473c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/ScriptCoinSC.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/ScriptCoinSC.cs new file mode 100644 index 0000000..4f1f456 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/ScriptCoinSC.cs @@ -0,0 +1,188 @@ +using System; +using NBitcoin; + +/// +/// Represent a coin which need a redeem script to be spent (P2SH or P2WSH) +/// +public class ScriptCoinSC : Coin +{ + public ScriptCoinSC() + { + + } + + public ScriptCoinSC(OutPoint fromOutpoint, TxOut fromTxOut, Script redeem) + : base(fromOutpoint, fromTxOut) + { + this.Redeem = redeem; + } + + internal ScriptCoinSC(Transaction fromTx, uint fromOutputIndex, Script redeem) + : base(fromTx, fromOutputIndex) + { + this.Redeem = redeem; + } + + internal ScriptCoinSC(Transaction fromTx, TxOut fromOutput, Script redeem) + : base(fromTx, fromOutput) + { + this.Redeem = redeem; + } + + internal ScriptCoinSC(ICoin coin, Script redeem) + : base(coin.Outpoint, coin.TxOut) + { + this.Redeem = redeem; + } + + internal ScriptCoinSC(IndexedTxOut txOut, Script redeem) + : base(txOut) + { + this.Redeem = redeem; + } + + internal ScriptCoinSC(uint256 txHash, uint outputIndex, Money amount, Script scriptPubKey, Script redeem) + : base(txHash, outputIndex, amount, scriptPubKey) + { + this.Redeem = redeem; + } + + public static ScriptCoinSC Create(Network network, OutPoint fromOutpoint, TxOut fromTxOut, Script redeem) + { + return new ScriptCoinSC(fromOutpoint, fromTxOut, redeem).AssertCoherent(network); + } + + public static ScriptCoinSC Create(Network network, Transaction fromTx, uint fromOutputIndex, Script redeem) + { + return new ScriptCoinSC(fromTx, fromOutputIndex, redeem).AssertCoherent(network); + } + + public static ScriptCoinSC Create(Network network, Transaction fromTx, TxOut fromOutput, Script redeem) + { + return new ScriptCoinSC(fromTx, fromOutput, redeem).AssertCoherent(network); + } + + public static ScriptCoinSC Create(Network network, ICoin coin, Script redeem) + { + return new ScriptCoinSC(coin, redeem).AssertCoherent(network); + } + + public static ScriptCoinSC Create(Network network, IndexedTxOut txOut, Script redeem) + { + return new ScriptCoinSC(txOut, redeem).AssertCoherent(network); + } + + public static ScriptCoinSC Create(Network network, uint256 txHash, uint outputIndex, Money amount, Script scriptPubKey, Script redeem) + { + return new ScriptCoinSC(txHash, outputIndex, amount, scriptPubKey, redeem).AssertCoherent(network); + } + + public bool IsP2SH + { + get + { + return this.ScriptPubKey.ToBytes(true)[0] == (byte)OpcodeType.OP_HASH160; + } + } + + public Script GetP2SHRedeem() + { + if (!this.IsP2SH) + return null; + Script p2shRedeem = this.RedeemType == RedeemType.P2SH ? this.Redeem : + this.RedeemType == RedeemType.WitnessV0 ? this.Redeem.WitHash.ScriptPubKey : + null; + if (p2shRedeem == null) + throw new NotSupportedException("RedeemType not supported for getting the P2SH script, contact the library author"); + return p2shRedeem; + } + + public RedeemType RedeemType + { + get + { + return this.Redeem.Hash.ScriptPubKey == this.TxOut.ScriptPubKey ? + RedeemType.P2SH : + RedeemType.WitnessV0; + } + } + + public ScriptCoinSC AssertCoherent(Network network) + { + if (this.Redeem == null) + throw new ArgumentException("redeem cannot be null", "redeem"); + + TxDestination expectedDestination = GetRedeemHash(network, this.TxOut.ScriptPubKey); + if (expectedDestination == null) + { + throw new ArgumentException("the provided scriptPubKey is not P2SH or P2WSH"); + } + if (expectedDestination is ScriptId) + { + if (PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(this.Redeem)) + { + throw new ArgumentException("The redeem script provided must be the witness one, not the P2SH one"); + } + + if (expectedDestination.ScriptPubKey != this.Redeem.Hash.ScriptPubKey) + { + if (this.Redeem.WitHash.ScriptPubKey.Hash.ScriptPubKey != expectedDestination.ScriptPubKey) + throw new ArgumentException("The redeem provided does not match the scriptPubKey of the coin"); + } + } + else if (expectedDestination is WitScriptId) + { + if (expectedDestination.ScriptPubKey != this.Redeem.WitHash.ScriptPubKey) + throw new ArgumentException("The redeem provided does not match the scriptPubKey of the coin"); + } + else + throw new NotSupportedException("Not supported redeemed scriptPubkey"); + + return this; + } + + + public Script Redeem + { + get; + set; + } + + public override Script GetScriptCode(Network network) + { + if (!CanGetScriptCode(network)) + throw new InvalidOperationException("You need to provide the P2WSH redeem script with ScriptCoinSC.ToScriptCoin()"); + if (this._OverrideScriptCode != null) + return this._OverrideScriptCode; + WitKeyId key = PayToWitPubKeyHashTemplate.Instance.ExtractScriptPubKeyParameters(network, this.Redeem); + if (key != null) + return key.AsKeyId().ScriptPubKey; + return this.Redeem; + } + + public override bool CanGetScriptCode(Network network) + { + return this._OverrideScriptCode != null || !this.IsP2SH || !PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(this.Redeem); + } + + public override HashVersion GetHashVersion(Network network) + { + bool isWitness = PayToWitTemplate.Instance.CheckScriptPubKey(this.ScriptPubKey) || + PayToWitTemplate.Instance.CheckScriptPubKey(this.Redeem) || this.RedeemType == RedeemType.WitnessV0; + return isWitness ? HashVersion.Witness : HashVersion.Original; + } + + /// + /// Returns the hash contained in the scriptPubKey (P2SH or P2WSH) + /// + /// The scriptPubKey + /// The hash of the scriptPubkey + public static TxDestination GetRedeemHash(Network network, Script scriptPubKey) + { + if (scriptPubKey == null) + throw new ArgumentNullException("scriptPubKey"); + return PayToScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey) as TxDestination + ?? + PayToWitScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey); + } +} diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/ScriptCoinSC.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/ScriptCoinSC.cs.meta new file mode 100644 index 0000000..fbb3e46 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/ScriptCoinSC.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f72a389051a97794aa1f07ff8f2727ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/TxBuilder.cs b/Src/StratisUnity3d/Assets/Code/Dependencies/TxBuilder.cs new file mode 100644 index 0000000..49c3b45 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/TxBuilder.cs @@ -0,0 +1,2081 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NBitcoin.BuilderExtensions; +using NBitcoin.Crypto; +using NBitcoin.OpenAsset; +using NBitcoin.Policy; +using NBitcoin.Stealth; +using Builder = System.Func; + +namespace NBitcoin +{ + [Flags] + public enum ChangeType : int + { + All = 3, + Colored = 1, + Uncolored = 2 + } + public interface ICoinSelector + { + IEnumerable Select(IEnumerable coins, IMoney target); + } + + /// + /// A coin selector that selects all the coins passed by default. + /// Useful when a user wants a specific set of coins to be spent. + /// + public class AllCoinsSelector : ICoinSelector + { + public IEnumerable Select(IEnumerable coins, IMoney target) + { + return coins; + } + } + + /// + /// Algorithm implemented by bitcoin core https://github.com/bitcoin/bitcoin/blob/master/src/wallet.cpp#L1276 + /// Minimize the change + /// + public class DefaultCoinSelector : ICoinSelector + { + public DefaultCoinSelector() + { + + } + + private Random _Rand = new Random(); + public DefaultCoinSelector(int seed) + { + this._Rand = new Random(seed); + } + + /// + /// Select all coins belonging to same scriptPubKey together to protect privacy. (Default: true) + /// + public bool GroupByScriptPubKey + { + get; set; + } = true; + + #region ICoinSelector Members + + public IEnumerable Select(IEnumerable coins, IMoney target) + { + IMoney zero = target.Sub(target); + + var result = new List(); + IMoney total = zero; + + if (target.CompareTo(zero) == 0) + return result; + + var orderedCoinGroups = coins.GroupBy(c => this.GroupByScriptPubKey ? c.TxOut.ScriptPubKey : new Key().ScriptPubKey) + .Select(scriptPubKeyCoins => new + { + Amount = scriptPubKeyCoins.Select(c => c.Amount).Sum(zero), + Coins = scriptPubKeyCoins.ToList() + }).OrderBy(c => c.Amount); + + + var targetCoin = orderedCoinGroups + .FirstOrDefault(c => c.Amount.CompareTo(target) == 0); + //If any of your UTXO² matches the Targetı it will be used. + if (targetCoin != null) + return targetCoin.Coins; + + foreach (var coinGroup in orderedCoinGroups) + { + if (coinGroup.Amount.CompareTo(target) == -1 && total.CompareTo(target) == -1) + { + total = total.Add(coinGroup.Amount); + result.AddRange(coinGroup.Coins); + //If the "sum of all your UTXO smaller than the Target" happens to match the Target, they will be used. (This is the case if you sweep a complete wallet.) + if (total.CompareTo(target) == 0) + return result; + + } + else + { + if (total.CompareTo(target) == -1 && coinGroup.Amount.CompareTo(target) == 1) + { + //If the "sum of all your UTXO smaller than the Target" doesn't surpass the target, the smallest UTXO greater than your Target will be used. + return coinGroup.Coins; + } + else + { + // Else Bitcoin Core does 1000 rounds of randomly combining unspent transaction outputs until their sum is greater than or equal to the Target. If it happens to find an exact match, it stops early and uses that. + // Otherwise it finally settles for the minimum of + // the smallest UTXO greater than the Target + // the smallest combination of UTXO it discovered in Step 4. + var allCoins = orderedCoinGroups.ToArray(); + IMoney minTotal = null; + + + for (int _ = 0; _ < 1000; _++) + { + var selection = new List(); + Utils.Shuffle(allCoins, this._Rand); + total = zero; + for (int i = 0; i < allCoins.Length; i++) + { + selection.AddRange(allCoins[i].Coins); + total = total.Add(allCoins[i].Amount); + if (total.CompareTo(target) == 0) + return selection; + if (total.CompareTo(target) == 1) + break; + } + if (total.CompareTo(target) == -1) + { + return null; + } + if (minTotal == null || total.CompareTo(minTotal) == -1) + { + minTotal = total; + result = selection; + } + } + + if (minTotal != null) + { + total = minTotal; + } + break; + } + } + } + + if (total.CompareTo(target) == -1) + return null; + + // optimize the set of used coins, removing unnecessary small inputs + List sortedUsedCoins = result.OrderBy(c => c.Amount).ToList(); + IMoney excess = total.Sub(target); + for (int i = 0; i < sortedUsedCoins.Count; i++) + { + // if the smallest coin in the set is smaller or equal to the difference between the excess of + // the total and its amount, we can safely remove it + ICoin coin = sortedUsedCoins[i]; + if (coin.Amount.CompareTo(excess) <= 0) + { + result.Remove(coin); + excess = excess.Sub(coin.Amount); + } + + if (excess.CompareTo(zero) <= 0) + break; + } + + return result; + } + + #endregion + } + + /// + /// Exception thrown when not enough funds are present for verifying or building a transaction + /// + public class NotEnoughFundsException : Exception + { + public NotEnoughFundsException(string message, string group, IMoney missing) + : base(BuildMessage(message, group, missing)) + { + this.Missing = missing; + this.Group = group; + } + + private static string BuildMessage(string message, string group, IMoney missing) + { + var builder = new StringBuilder(); + builder.Append(message); + if (group != null) + builder.Append(" in group " + group); + if (missing != null) + builder.Append(" with missing amount " + missing); + return builder.ToString(); + } + public NotEnoughFundsException(string message, Exception inner) + : base(message, inner) + { + } + + /// + /// The group name who is missing the funds + /// + public string Group + { + get; + set; + } + + /// + /// Amount of Money missing + /// + public IMoney Missing + { + get; + set; + } + } + + /// + /// A class for building and signing all sort of transactions easily (http://www.codeproject.com/Articles/835098/NBitcoin-Build-Them-All) + /// + public class TransactionBuilderSC + { + internal class TransactionBuilderSigner : ISigner + { + private ICoin coin; + private SigHash sigHash; + private IndexedTxIn txIn; + private TransactionBuilderSC _builderSc; + public TransactionBuilderSigner(TransactionBuilderSC builderSc, ICoin coin, SigHash sigHash, IndexedTxIn txIn) + { + this._builderSc = builderSc; + this.coin = coin; + this.sigHash = sigHash; + this.txIn = txIn; + } + #region ISigner Members + + public TransactionSignature Sign(Key key) + { + return this.txIn.Sign(this._builderSc.Network, key, this.coin, this.sigHash); + } + + #endregion + } + + internal class TransactionBuilderKeyRepository : IKeyRepository + { + private TransactionSigningContext _Ctx; + private TransactionBuilderSC _txBuilderSc; + public TransactionBuilderKeyRepository(TransactionBuilderSC txBuilderSc, TransactionSigningContext ctx) + { + this._Ctx = ctx; + this._txBuilderSc = txBuilderSc; + } + #region IKeyRepository Members + + public Key FindKey(Script scriptPubkey) + { + return this._txBuilderSc.FindKey(this._Ctx, scriptPubkey); + } + + #endregion + } + + private class KnownSignatureSigner : ISigner, IKeyRepository + { + private ICoin coin; + private SigHash sigHash; + private IndexedTxIn txIn; + private List> _KnownSignatures; + private Dictionary _VerifiedSignatures = new Dictionary(); + private Dictionary _DummyToRealKey = new Dictionary(); + private TransactionBuilderSC _builderSc; + + public KnownSignatureSigner(TransactionBuilderSC builderSc, List> _KnownSignatures, ICoin coin, SigHash sigHash, IndexedTxIn txIn) + { + this._builderSc = builderSc; + this._KnownSignatures = _KnownSignatures; + this.coin = coin; + this.sigHash = sigHash; + this.txIn = txIn; + } + + public Key FindKey(Script scriptPubKey) + { + foreach (Tuple tv in this._KnownSignatures.Where(tv => IsCompatibleKey(tv.Item1, scriptPubKey))) + { + uint256 hash = this.txIn.GetSignatureHash(this._builderSc.Network, this.coin, this.sigHash); + if (tv.Item1.Verify(hash, tv.Item2)) + { + var key = new Key(); + this._DummyToRealKey.Add(Hashes.Hash256(key.PubKey.ToBytes()), tv.Item1); + this._VerifiedSignatures.AddOrReplace(key.PubKey.Hash, tv.Item2); + return key; + } + } + return null; + } + + public Script ReplaceDummyKeys(Script script) + { + List ops = script.ToOps().ToList(); + var result = new List(); + foreach (Op op in ops) + { + uint256 h = Hashes.Hash256(op.PushData); + PubKey real; + if (this._DummyToRealKey.TryGetValue(h, out real)) + result.Add(Op.GetPushOp(real.ToBytes())); + else + result.Add(op); + } + return new Script(result.ToArray()); + } + + public TransactionSignature Sign(Key key) + { + return new TransactionSignature(this._VerifiedSignatures[key.PubKey.Hash], this.sigHash); + } + } + + internal class TransactionSigningContext + { + public TransactionSigningContext(TransactionBuilderSC builderSc, Transaction transaction) + { + this.BuilderSc = builderSc; + this.Transaction = transaction; + } + + public Transaction Transaction + { + get; + set; + } + public TransactionBuilderSC BuilderSc + { + get; + set; + } + + private readonly List _AdditionalKeys = new List(); + public List AdditionalKeys + { + get + { + return this._AdditionalKeys; + } + } + + public SigHash SigHash + { + get; + set; + } + } + + internal class TransactionBuildingContext + { + public TransactionBuildingContext(TransactionBuilderSC builderSc) + { + this.BuilderSc = builderSc; + this.Transaction = builderSc.Network.CreateTransaction(); + this.AdditionalFees = Money.Zero; + } + + public BuilderGroup Group + { + get; + set; + } + + private readonly List _ConsumedCoins = new List(); + public List ConsumedCoins + { + get + { + return this._ConsumedCoins; + } + } + + public TransactionBuilderSC BuilderSc + { + get; + set; + } + + public Transaction Transaction + { + get; + set; + } + + public Money AdditionalFees + { + get; + set; + } + + private readonly List _AdditionalBuilders = new List(); + public List AdditionalBuilders + { + get + { + return this._AdditionalBuilders; + } + } + + private ColorMarker _Marker; + + public ColorMarker GetColorMarker(bool issuance) + { + if (this._Marker == null) this._Marker = new ColorMarker(); + if (!issuance) + EnsureMarkerInserted(); + return this._Marker; + } + + private TxOut EnsureMarkerInserted() + { + uint position; + TxIn dummy = this.Transaction.AddInput(new TxIn(new OutPoint(new uint256(1), 0))); //Since a transaction without input will be considered without marker, insert a dummy + try + { + if (ColorMarker.Get(this.Transaction, out position) != null) + return this.Transaction.Outputs[position]; + } + finally + { + this.Transaction.Inputs.Remove(dummy); + } + TxOut txout = this.Transaction.AddOutput(new TxOut() + { + ScriptPubKey = new ColorMarker().GetScript() + }); + txout.Value = Money.Zero; + return txout; + } + + public void Finish() + { + if (this._Marker != null) + { + TxOut txout = EnsureMarkerInserted(); + txout.ScriptPubKey = this._Marker.GetScript(); + } + } + + public IssuanceCoin IssuanceCoin + { + get; + set; + } + + public IMoney ChangeAmount + { + get; + set; + } + + public TransactionBuildingContext CreateMemento() + { + var memento = new TransactionBuildingContext(this.BuilderSc); + memento.RestoreMemento(this); + return memento; + } + + public void RestoreMemento(TransactionBuildingContext memento) + { + this._Marker = memento._Marker == null ? null : new ColorMarker(memento._Marker.GetScript()); + this.Transaction = memento.BuilderSc.Network.CreateTransaction(memento.Transaction.ToBytes()); + this.AdditionalFees = memento.AdditionalFees; + } + + public bool NonFinalSequenceSet + { + get; + set; + } + + public IMoney CoverOnly + { + get; + set; + } + + public IMoney Dust + { + get; + set; + } + + public ChangeType ChangeType + { + get; + set; + } + } + + internal class BuilderGroup + { + private TransactionBuilderSC _Parent; + public BuilderGroup(TransactionBuilderSC parent) + { + this._Parent = parent; + this.FeeWeight = 1.0m; + this.Builders.Add(SetChange); + } + + private IMoney SetChange(TransactionBuildingContext ctx) + { + var changeAmount = (Money)ctx.ChangeAmount; + if (changeAmount.Satoshi == 0) + return Money.Zero; + ctx.Transaction.AddOutput(new TxOut(changeAmount, ctx.Group.ChangeScript[(int)ChangeType.Uncolored])); + return changeAmount; + } + + internal List Builders = new List(); + internal Dictionary Coins = new Dictionary(); + internal List IssuanceBuilders = new List(); + internal Dictionary> BuildersByAsset = new Dictionary>(); + internal Script[] ChangeScript = new Script[3]; + + internal void Shuffle() + { + Shuffle(this.Builders); + foreach (KeyValuePair> builders in this.BuildersByAsset) + Shuffle(builders.Value); + Shuffle(this.IssuanceBuilders); + } + + private void Shuffle(List builders) + { + Utils.Shuffle(builders, this._Parent._Rand); + } + + public Money CoverOnly + { + get; + set; + } + + public string Name + { + get; + set; + } + + public decimal FeeWeight + { + get; + set; + } + } + + private List _BuilderGroups = new List(); + private BuilderGroup _CurrentGroup = null; + internal BuilderGroup CurrentGroup + { + get + { + if (this._CurrentGroup == null) + { + this._CurrentGroup = new BuilderGroup(this); + this._BuilderGroups.Add(this._CurrentGroup); + } + return this._CurrentGroup; + } + } + + public TransactionBuilderSC(Network network) + { + this.Network = network; + + this._Rand = new Random(); + this.CoinSelector = new DefaultCoinSelector(); + this.StandardTransactionPolicy = new StandardTransactionPolicy(this.Network); + this.DustPrevention = true; + InitExtensions(); + } + + private void InitExtensions() + { + this.Extensions.Add(new P2PKHBuilderExtension()); + this.Extensions.Add(new P2MultiSigBuilderExtension()); + this.Extensions.Add(new P2FederationBuilderExtension()); + this.Extensions.Add(new P2PKBuilderExtension()); + this.Extensions.Add(new OPTrueExtension()); + } + + internal Random _Rand; + + public TransactionBuilderSC(int seed, Network network) + { + this.Network = network; + + this._Rand = new Random(seed); + this.CoinSelector = new DefaultCoinSelector(seed); + this.StandardTransactionPolicy = new StandardTransactionPolicy(this.Network); + this.DustPrevention = true; + InitExtensions(); + } + + public ICoinSelector CoinSelector + { + get; + set; + } + + /// + /// This field should be mandatory in the constructor. + /// + public Network Network + { + get; + private set; + } + + /// + /// Will transform transfers below Dust, so the transaction get correctly relayed by the network. + /// If true, it will remove any TxOut below Dust, so the transaction get correctly relayed by the network. (Default: true) + /// + public bool DustPrevention + { + get; + set; + } + + /// + /// If true, the TransactionBuilder will not select coins whose fee to spend is higher than its value. (Default: true) + /// The cost of spending a coin is based on the . + /// + public bool FilterUneconomicalCoins { get; set; } = true; + + /// + /// If is true, this rate is used to know if an output is economical. + /// This property is set automatically when calling or . + /// + public FeeRate FilterUneconomicalCoinsRate + { + get; set; + } + + /// + /// A callback used by the TransactionBuilder when it does not find the coin for an input + /// + public Func CoinFinder + { + get; + set; + } + + /// + /// A callback used by the TransactionBuilder when it does not find the key for a scriptPubKey + /// + public Func KeyFinder + { + get; + set; + } + + private LockTime? _LockTime; + public TransactionBuilderSC SetLockTime(LockTime lockTime) + { + this._LockTime = lockTime; + return this; + } + + private uint? _TimeStamp; + public TransactionBuilderSC SetTimeStamp(uint timeStamp) + { + this._TimeStamp = timeStamp; + return this; + } + + private List _Keys = new List(); + + public TransactionBuilderSC AddKeys(params ISecret[] keys) + { + AddKeys(keys.Select(k => k.PrivateKey).ToArray()); + return this; + } + + public TransactionBuilderSC AddKeys(params Key[] keys) + { + this._Keys.AddRange(keys); + foreach (Key k in keys) + { + AddKnownRedeems(k.PubKey.ScriptPubKey); + AddKnownRedeems(k.PubKey.WitHash.ScriptPubKey); + AddKnownRedeems(k.PubKey.Hash.ScriptPubKey); + } + return this; + } + + public TransactionBuilderSC AddKnownSignature(PubKey pubKey, TransactionSignature signature) + { + if (pubKey == null) + throw new ArgumentNullException("pubKey"); + if (signature == null) + throw new ArgumentNullException("signature"); + this._KnownSignatures.Add(Tuple.Create(pubKey, signature.Signature)); + return this; + } + + public TransactionBuilderSC AddKnownSignature(PubKey pubKey, ECDSASignature signature) + { + if (pubKey == null) + throw new ArgumentNullException("pubKey"); + if (signature == null) + throw new ArgumentNullException("signature"); + this._KnownSignatures.Add(Tuple.Create(pubKey, signature)); + return this; + } + + public TransactionBuilderSC AddCoins(params ICoin[] coins) + { + return AddCoins((IEnumerable)coins); + } + + public TransactionBuilderSC AddCoins(IEnumerable coins) + { + foreach (ICoin coin in coins) + { + this.CurrentGroup.Coins.AddOrReplace(coin.Outpoint, coin); + } + return this; + } + + /// + /// Set the name of this group (group are separated by call to Then()) + /// + /// Name of the group + /// + public TransactionBuilderSC SetGroupName(string groupName) + { + this.CurrentGroup.Name = groupName; + return this; + } + + /// + /// Send bitcoins to a destination + /// + /// The destination + /// The amount + /// + public TransactionBuilderSC Send(IDestination destination, Money amount) + { + return Send(destination.ScriptPubKey, amount); + } + + private readonly static TxNullDataTemplate _OpReturnTemplate = new TxNullDataTemplate(1024 * 1024); + + /// + /// Send bitcoins to a destination + /// + /// The destination + /// The amount + /// + public TransactionBuilderSC Send(Script scriptPubKey, Money amount, bool ignoreFees = false) + { + if (amount < Money.Zero) + throw new ArgumentOutOfRangeException("amount", "amount can't be negative"); + this._LastSendBuilder = null; //If the amount is dust, we don't want the fee to be paid by the previous Send + if (!ignoreFees && this.DustPrevention && amount < GetDust(scriptPubKey) && !_OpReturnTemplate.CheckScriptPubKey(scriptPubKey)) + { + SendFees(amount); + return this; + } + + var builder = new SendBuilder(new TxOut(amount, scriptPubKey)); + this.CurrentGroup.Builders.Add(builder.Build); + this._LastSendBuilder = builder; + return this; + } + + + /// + /// Clears send builders so we can setup recipient sends again. + /// + public void ClearSendBuilders() + { + for (int i = this.CurrentGroup.Builders.Count - 1; i >= 0; i--) + { + // All other builders have different return type. + if (this.CurrentGroup.Builders[i].Method.ReturnType == typeof(Money)) + { + this.CurrentGroup.Builders.RemoveAt(i); + } + } + } + + private SendBuilder _LastSendBuilder; + private SendBuilder _SubstractFeeBuilder; + + private class SendBuilder + { + internal TxOut _TxOut; + + public SendBuilder(TxOut txout) + { + this._TxOut = txout; + } + + public Money Build(TransactionBuildingContext ctx) + { + ctx.Transaction.Outputs.Add(this._TxOut); + return this._TxOut.Value; + } + } + + /// + /// Will subtract fees from the previous TxOut added by the last TransactionBuidler.Send() call + /// + /// + public TransactionBuilderSC SubtractFees() + { + if (this._LastSendBuilder == null) + throw new InvalidOperationException("No call to TransactionBuilder.Send has been done which can support the fees"); + this._SubstractFeeBuilder = this._LastSendBuilder; + return this; + } + + /// + /// Send a money amount to the destination + /// + /// The destination + /// The amount (supported : Money, AssetMoney, MoneyBag) + /// + /// The coin type is not supported + public TransactionBuilderSC Send(IDestination destination, IMoney amount) + { + return Send(destination.ScriptPubKey, amount); + } + + /// + /// Send a money amount to the destination + /// + /// The destination + /// The amount (supported : Money, AssetMoney, MoneyBag) + /// + /// The coin type is not supported + public TransactionBuilderSC Send(Script scriptPubKey, IMoney amount) + { + var bag = amount as MoneyBag; + if (bag != null) + { + foreach (IMoney money in bag) + Send(scriptPubKey, amount); + return this; + } + + var coinAmount = amount as Money; + if (coinAmount != null) + return Send(scriptPubKey, coinAmount); + + var assetAmount = amount as AssetMoney; + if (assetAmount != null) + return SendAsset(scriptPubKey, assetAmount); + + throw new NotSupportedException("Type of Money not supported"); + } + + /// + /// Send assets (Open Asset) to a destination + /// + /// The destination + /// The asset and amount + /// + public TransactionBuilderSC SendAsset(IDestination destination, AssetMoney asset) + { + return SendAsset(destination.ScriptPubKey, asset); + } + + /// + /// Send assets (Open Asset) to a destination + /// + /// The destination + /// The asset and amount + /// + public TransactionBuilderSC SendAsset(IDestination destination, AssetId assetId, ulong quantity) + { + return SendAsset(destination, new AssetMoney(assetId, quantity)); + } + + public TransactionBuilderSC Shuffle() + { + Utils.Shuffle(this._BuilderGroups, this._Rand); + foreach (BuilderGroup group in this._BuilderGroups) + group.Shuffle(); + return this; + } + + private IMoney SetColoredChange(TransactionBuildingContext ctx) + { + var changeAmount = (AssetMoney)ctx.ChangeAmount; + if (changeAmount.Quantity == 0) + return changeAmount; + + ColorMarker marker = ctx.GetColorMarker(false); + Script script = ctx.Group.ChangeScript[(int)ChangeType.Colored]; + TxOut txout = ctx.Transaction.AddOutput(new TxOut(GetDust(script), script)); + marker.SetQuantity(ctx.Transaction.Outputs.Count - 2, changeAmount.Quantity); + ctx.AdditionalFees += txout.Value; + return changeAmount; + } + + public TransactionBuilderSC SendAsset(Script scriptPubKey, AssetId assetId, ulong assetQuantity) + { + return SendAsset(scriptPubKey, new AssetMoney(assetId, assetQuantity)); + } + + public TransactionBuilderSC SendAsset(Script scriptPubKey, AssetMoney asset) + { + if (asset.Quantity < 0) + throw new ArgumentOutOfRangeException("asset", "Asset amount can't be negative"); + + if (asset.Quantity == 0) + return this; + + AssertOpReturn("Colored Coin"); + + List builders = this.CurrentGroup.BuildersByAsset.TryGet(asset.Id); + if (builders == null) + { + builders = new List(); + this.CurrentGroup.BuildersByAsset.Add(asset.Id, builders); + builders.Add(SetColoredChange); + } + + builders.Add(ctx => + { + ColorMarker marker = ctx.GetColorMarker(false); + TxOut txout = ctx.Transaction.AddOutput(new TxOut(GetDust(scriptPubKey), scriptPubKey)); + marker.SetQuantity(ctx.Transaction.Outputs.Count - 2, asset.Quantity); + ctx.AdditionalFees += txout.Value; + return asset; + }); + + return this; + } + + private Money GetDust() + { + return GetDust(new Script(new byte[25])); + } + + private Money GetDust(Script script) + { + if (this.StandardTransactionPolicy == null || this.StandardTransactionPolicy.MinRelayTxFee == null) + return Money.Zero; + return new TxOut(Money.Zero, script).GetDustThreshold(this.StandardTransactionPolicy.MinRelayTxFee); + } + + /// + /// Set transaction policy fluently + /// + /// The policy + /// this + public TransactionBuilderSC SetTransactionPolicy(StandardTransactionPolicy policy) + { + this.StandardTransactionPolicy = policy; + return this; + } + + public StandardTransactionPolicy StandardTransactionPolicy + { + get; + set; + } + + private string _OpReturnUser; + private void AssertOpReturn(string name) + { + if (this._OpReturnUser == null) + { + this._OpReturnUser = name; + } + else + { + if (this._OpReturnUser != name) + throw new InvalidOperationException("Op return already used for " + this._OpReturnUser); + } + } + + public TransactionBuilderSC Send(BitcoinStealthAddress address, Money amount, Key ephemKey = null) + { + if (amount < Money.Zero) + throw new ArgumentOutOfRangeException("amount", "amount can't be negative"); + + if (this._OpReturnUser == null) + this._OpReturnUser = "Stealth Payment"; + else + throw new InvalidOperationException("Op return already used for " + this._OpReturnUser); + + this.CurrentGroup.Builders.Add(ctx => + { + StealthPayment payment = address.CreatePayment(ephemKey); + payment.AddToTransaction(ctx.Transaction, amount); + return amount; + }); + return this; + } + + public TransactionBuilderSC IssueAsset(IDestination destination, AssetMoney asset) + { + return IssueAsset(destination.ScriptPubKey, asset); + } + + private AssetId _IssuedAsset; + + public TransactionBuilderSC IssueAsset(Script scriptPubKey, AssetMoney asset) + { + AssertOpReturn("Colored Coin"); + + if (this._IssuedAsset == null) + this._IssuedAsset = asset.Id; + else if (this._IssuedAsset != asset.Id) + throw new InvalidOperationException("You can issue only one asset type in a transaction"); + + this.CurrentGroup.IssuanceBuilders.Add(ctx => + { + ColorMarker marker = ctx.GetColorMarker(true); + if (ctx.IssuanceCoin == null) + { + IssuanceCoin issuance = ctx.Group.Coins.Values.OfType().Where(i => i.AssetId == asset.Id).FirstOrDefault(); + if (issuance == null) + throw new InvalidOperationException("No issuance coin for emitting asset found"); + ctx.IssuanceCoin = issuance; + ctx.Transaction.Inputs.Insert(0, new TxIn(issuance.Outpoint)); + ctx.AdditionalFees -= issuance.Bearer.Amount; + if (issuance.DefinitionUrl != null) + { + marker.SetMetadataUrl(issuance.DefinitionUrl); + } + } + + ctx.Transaction.Outputs.Insert(0, new TxOut(GetDust(scriptPubKey), scriptPubKey)); + marker.Quantities = new[] { checked((ulong)asset.Quantity) }.Concat(marker.Quantities).ToArray(); + ctx.AdditionalFees += ctx.Transaction.Outputs[0].Value; + return asset; + }); + return this; + } + + public TransactionBuilderSC SendFees(Money fees) + { + if (fees == null) + throw new ArgumentNullException("fees"); + this.CurrentGroup.Builders.Add(ctx => fees); + this._TotalFee += fees; + return this; + } + + private Money _TotalFee = Money.Zero; + + /// + /// Split the estimated fees across the several groups (separated by Then()) + /// + /// + /// + public TransactionBuilderSC SendEstimatedFees(FeeRate feeRate) + { + this.FilterUneconomicalCoinsRate = feeRate; + Money fee = EstimateFees(feeRate); + SendFees(fee); + return this; + } + + /// + /// Estimate the fee needed for the transaction, and split among groups according to their fee weight + /// + /// + /// + public TransactionBuilderSC SendEstimatedFeesSplit(FeeRate feeRate) + { + this.FilterUneconomicalCoinsRate = feeRate; + Money fee = EstimateFees(feeRate); + SendFeesSplit(fee); + return this; + } + + /// + /// Send the fee splitted among groups according to their fee weight + /// + /// + /// + public TransactionBuilderSC SendFeesSplit(Money fees) + { + if (fees == null) + throw new ArgumentNullException("fees"); + + BuilderGroup lastGroup = this.CurrentGroup; //Make sure at least one group exists + decimal totalWeight = this._BuilderGroups.Select(b => b.FeeWeight).Sum(); + Money totalSent = Money.Zero; + foreach (BuilderGroup group in this._BuilderGroups) + { + Money groupFee = Money.Satoshis((group.FeeWeight / totalWeight) * fees.Satoshi); + totalSent += groupFee; + if (this._BuilderGroups.Last() == group) + { + Money leftOver = fees - totalSent; + groupFee += leftOver; + } + group.Builders.Add(ctx => groupFee); + } + return this; + } + + /// + /// If using SendFeesSplit or SendEstimatedFeesSplit, determine the weight this group participate in paying the fees + /// + /// The weight of fee participation + /// + public TransactionBuilderSC SetFeeWeight(decimal feeWeight) + { + this.CurrentGroup.FeeWeight = feeWeight; + return this; + } + + public TransactionBuilderSC SetChange(IDestination destination, ChangeType changeType = ChangeType.All) + { + return SetChange(destination.ScriptPubKey, changeType); + } + + public TransactionBuilderSC SetChange(Script scriptPubKey, ChangeType changeType = ChangeType.All) + { + if ((changeType & ChangeType.Colored) != 0) + { + this.CurrentGroup.ChangeScript[(int)ChangeType.Colored] = scriptPubKey; + } + if ((changeType & ChangeType.Uncolored) != 0) + { + this.CurrentGroup.ChangeScript[(int)ChangeType.Uncolored] = scriptPubKey; + } + return this; + } + + public TransactionBuilderSC SetCoinSelector(ICoinSelector selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + this.CoinSelector = selector; + return this; + } + + /// + /// Build the transaction + /// + /// True if signs all inputs with the available keys + /// The transaction + /// Not enough funds are available + public Transaction BuildTransaction(bool sign) + { + return BuildTransaction(sign, SigHash.All); + } + + /// + /// Build the transaction + /// + /// True if signs all inputs with the available keys + /// The type of signature + /// The transaction + /// Not enough funds are available + public Transaction BuildTransaction(bool sign, SigHash sigHash) + { + var ctx = new TransactionBuildingContext(this); + + if (this._CompletedTransaction != null) + ctx.Transaction = this.Network.CreateTransaction(this._CompletedTransaction.ToBytes()); + + if (this._LockTime != null) + ctx.Transaction.LockTime = this._LockTime.Value; + + foreach (BuilderGroup group in this._BuilderGroups) + { + ctx.Group = group; + ctx.AdditionalBuilders.Clear(); + ctx.AdditionalFees = Money.Zero; + + ctx.ChangeType = ChangeType.Colored; + foreach (Builder builder in group.IssuanceBuilders) + builder(ctx); + + List>> buildersByAsset = group.BuildersByAsset.ToList(); + foreach (KeyValuePair> builders in buildersByAsset) + { + IEnumerable coins = group.Coins.Values.OfType().Where(c => c.Amount.Id == builders.Key); + + ctx.Dust = new AssetMoney(builders.Key); + ctx.CoverOnly = null; + ctx.ChangeAmount = new AssetMoney(builders.Key); + Money btcSpent = BuildTransaction(ctx, group, builders.Value, coins, new AssetMoney(builders.Key)) + .OfType().Select(c => c.Bearer.Amount).Sum(); + ctx.AdditionalFees -= btcSpent; + } + + ctx.AdditionalBuilders.Add(_ => _.AdditionalFees); + ctx.Dust = GetDust(); + ctx.ChangeAmount = Money.Zero; + ctx.CoverOnly = group.CoverOnly; + ctx.ChangeType = ChangeType.Uncolored; + BuildTransaction(ctx, group, group.Builders, group.Coins.Values.OfType().Where(IsEconomical), Money.Zero); + } + + ctx.Finish(); + + if (sign) + { + SignTransactionInPlace(ctx.Transaction, sigHash); + } + + return ctx.Transaction; + } + + private bool IsEconomical(Coin c) + { + if (!this.FilterUneconomicalCoins || this.FilterUneconomicalCoinsRate == null) + return true; + + int witSize = 0; + int baseSize = 0; + EstimateScriptSigSize(c, ref witSize, ref baseSize); + var vSize = witSize / this.Network.Consensus.Options.WitnessScaleFactor + baseSize; + + return c.Amount >= this.FilterUneconomicalCoinsRate.GetFee(vSize); + } + + private IEnumerable BuildTransaction( + TransactionBuildingContext ctx, + BuilderGroup group, + IEnumerable builders, + IEnumerable coins, + IMoney zero) + { + TransactionBuildingContext originalCtx = ctx.CreateMemento(); + Money fees = this._TotalFee + ctx.AdditionalFees; + + // Replace the _SubstractFeeBuilder by another one with the fees substracts + List builderList = builders.ToList(); + for (int i = 0; i < builderList.Count; i++) + { + if (builderList[i].Target == this._SubstractFeeBuilder) + { + builderList.Remove(builderList[i]); + TxOut newTxOut = this._SubstractFeeBuilder._TxOut.Clone(); + newTxOut.Value -= fees; + builderList.Insert(i, new SendBuilder(newTxOut).Build); + } + } + //////////////////////////////////////////////////////// + + IMoney target = builderList.Concat(ctx.AdditionalBuilders).Select(b => b(ctx)).Sum(zero); + if (ctx.CoverOnly != null) + { + target = ctx.CoverOnly.Add(ctx.ChangeAmount); + } + + IEnumerable unconsumed = coins.Where(c => ctx.ConsumedCoins.All(cc => cc.Outpoint != c.Outpoint)); + IEnumerable selection = this.CoinSelector.Select(unconsumed, target); + if (selection == null) + { + throw new NotEnoughFundsException("Not enough funds to cover the target", + group.Name, + target.Sub(unconsumed.Select(u => u.Amount).Sum(zero)) + ); + } + + IMoney total = selection.Select(s => s.Amount).Sum(zero); + IMoney change = total.Sub(target); + if (change.CompareTo(zero) == -1) + { + throw new NotEnoughFundsException("Not enough funds to cover the target", + group.Name, + change.Negate() + ); + } + + if (change.CompareTo(ctx.Dust) == 1) + { + Script changeScript = group.ChangeScript[(int)ctx.ChangeType]; + + if (changeScript == null) + throw new InvalidOperationException("A change address should be specified (" + ctx.ChangeType + ")"); + + if (!(ctx.Dust is Money) || change.CompareTo(GetDust(changeScript)) == 1) + { + ctx.RestoreMemento(originalCtx); + ctx.ChangeAmount = change; + try + { + return BuildTransaction(ctx, group, builders, coins, zero); + } + finally + { + ctx.ChangeAmount = zero; + } + } + } + + foreach (ICoin coin in selection) + { + ctx.ConsumedCoins.Add(coin); + TxIn input = ctx.Transaction.Inputs.FirstOrDefault(i => i.PrevOut == coin.Outpoint); + if (input == null) + input = ctx.Transaction.AddInput(new TxIn(coin.Outpoint)); + if (this._LockTime != null && !ctx.NonFinalSequenceSet) + { + input.Sequence = 0; + ctx.NonFinalSequenceSet = true; + } + } + + return selection; + } + + public Transaction SignTransaction(Transaction transaction, SigHash sigHash) + { + Transaction tx = this.Network.CreateTransaction(transaction.ToBytes()); + SignTransactionInPlace(tx, sigHash); + return tx; + } + + public Transaction SignTransaction(Transaction transaction) + { + return SignTransaction(transaction, SigHash.All); + } + + public Transaction SignTransactionInPlace(Transaction transaction) + { + return SignTransactionInPlace(transaction, SigHash.All); + } + + public Transaction SignTransactionInPlace(Transaction transaction, SigHash sigHash) + { + var ctx = new TransactionSigningContext(this, transaction) + { + SigHash = sigHash + }; + + foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs()) + { + ICoin coin = FindSignableCoin(input); + if (coin != null) + { + Sign(ctx, coin, input); + } + } + return transaction; + } + + public ICoin FindSignableCoin(IndexedTxIn txIn) + { + ICoin coin = FindCoin(txIn.PrevOut); + + if (coin is IColoredCoin) + coin = ((IColoredCoin)coin).Bearer; + + if (coin == null || coin is ScriptCoin || coin is StealthCoin) + return coin; + + TxDestination hash = ScriptCoin.GetRedeemHash(this.Network, coin.TxOut.ScriptPubKey); + if (hash != null) + { + Script redeem = this._ScriptPubKeyToRedeem.TryGet(coin.TxOut.ScriptPubKey); + if (redeem != null && PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(redeem)) + redeem = this._ScriptPubKeyToRedeem.TryGet(redeem); + if (redeem == null) + { + if (hash is WitScriptId) + redeem = PayToWitScriptHashTemplate.Instance.ExtractWitScriptParameters(txIn.WitScript, (WitScriptId)hash); + if (hash is ScriptId) + { + PayToScriptHashSigParameters parameters = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.Network, txIn.ScriptSig, (ScriptId)hash); + if (parameters != null) + redeem = parameters.RedeemScript; + } + } + if (redeem != null) + return new ScriptCoinSC(coin, redeem); + } + return coin; + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// The transaction to check + /// True if no error + public bool Verify(Transaction tx) + { + TransactionPolicyError[] errors; + return Verify(tx, null as Money, out errors); + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// The transaction to check + /// The expected fees (more or less 10%) + /// True if no error + public bool Verify(Transaction tx, Money expectedFees) + { + TransactionPolicyError[] errors; + return Verify(tx, expectedFees, out errors); + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// The transaction to check + /// The expected fee rate + /// True if no error + public bool Verify(Transaction tx, FeeRate expectedFeeRate) + { + TransactionPolicyError[] errors; + return Verify(tx, expectedFeeRate, out errors); + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// The transaction to check + /// Detected errors + /// True if no error + public bool Verify(Transaction tx, out TransactionPolicyError[] errors) + { + return Verify(tx, null as Money, out errors); + } + + /// + /// Verify that a transaction is fully signed, have enough fees, and follow the Standard and Miner Transaction Policy rules + /// + /// The transaction to check + /// The expected fees (more or less 10%) + /// Detected errors + /// True if no error + public bool Verify(Transaction tx, Money expectedFees, out TransactionPolicyError[] errors) + { + if (tx == null) + throw new ArgumentNullException("tx"); + ICoin[] coins = tx.Inputs.Select(i => FindCoin(i.PrevOut)).Where(c => c != null).ToArray(); + var exceptions = new List(); + TransactionPolicyError[] policyErrors = MinerTransactionPolicy.Instance.Check(tx, coins); + exceptions.AddRange(policyErrors); + policyErrors = this.StandardTransactionPolicy.Check(tx, coins); + exceptions.AddRange(policyErrors); + if (expectedFees != null) + { + Money fees = tx.GetFee(coins); + if (fees != null) + { + Money margin = Money.Zero; + if (this.DustPrevention) + margin = GetDust() * 2; + if (!fees.Almost(expectedFees, margin)) + exceptions.Add(new NotEnoughFundsPolicyError("Fees different than expected", expectedFees - fees)); + } + } + errors = exceptions.ToArray(); + return errors.Length == 0; + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// The transaction to check + /// The expected fee rate + /// Detected errors + /// True if no error + public bool Verify(Transaction tx, FeeRate expectedFeeRate, out TransactionPolicyError[] errors) + { + if (tx == null) + throw new ArgumentNullException("tx"); + return Verify(tx, expectedFeeRate == null ? null : expectedFeeRate.GetFee(tx, this.Network.Consensus.Options.WitnessScaleFactor), out errors); + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// he transaction to check + /// The expected fee rate + /// Detected errors + public TransactionPolicyError[] Check(Transaction tx, FeeRate expectedFeeRate) + { + return Check(tx, expectedFeeRate == null ? null : expectedFeeRate.GetFee(tx, this.Network.Consensus.Options.WitnessScaleFactor)); + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// he transaction to check + /// The expected fee + /// Detected errors + public TransactionPolicyError[] Check(Transaction tx, Money expectedFee) + { + TransactionPolicyError[] errors; + Verify(tx, expectedFee, out errors); + return errors; + } + + /// + /// Verify that a transaction is fully signed and have enough fees + /// + /// he transaction to check + /// Detected errors + public TransactionPolicyError[] Check(Transaction tx) + { + return Check(tx, null as Money); + } + + private CoinNotFoundException CoinNotFound(IndexedTxIn txIn) + { + return new CoinNotFoundException(txIn); + } + + public ICoin FindCoin(OutPoint outPoint) + { + ICoin result = this._BuilderGroups.Select(c => c.Coins.TryGet(outPoint)).FirstOrDefault(r => r != null); + if (result == null && this.CoinFinder != null) + result = this.CoinFinder(outPoint); + return result; + } + + /// + /// Find spent coins of a transaction + /// + /// The transaction + /// Array of size tx.Input.Count, if a coin is not fund, a null coin is returned. + public ICoin[] FindSpentCoins(Transaction tx) + { + return + tx + .Inputs + .Select(i => FindCoin(i.PrevOut)) + .ToArray(); + } + + /// + /// Estimate the physical size of the transaction + /// + /// The transaction to be estimated + /// The estimated size of the transaction in bytes. + public int EstimateSize(Transaction tx) + { + return EstimateSize(tx, false); + } + + /// + /// Estimate the size of the transaction + /// + /// The transaction to be estimated + /// If true, returns the size on which fee calculation are based, else returns the physical byte size + /// + public int EstimateSize(Transaction tx, bool virtualSize) + { + if (tx == null) + throw new ArgumentNullException("tx"); + + Transaction clone = this.Network.CreateTransaction(tx.ToHex()); + clone.Inputs.Clear(); + int baseSize = clone.GetSerializedSize(); + + int witSize = 0; + if (tx.HasWitness) + witSize += 2; + foreach (var txin in tx.Inputs.AsIndexedInputs()) + { + ICoin coin = FindSignableCoin(txin) ?? FindCoin(txin.PrevOut); + if (coin == null) + throw CoinNotFound(txin); + EstimateScriptSigSize(coin, ref witSize, ref baseSize); + baseSize += 41; + } + + return (virtualSize ? witSize / this.Network.Consensus.Options.WitnessScaleFactor + baseSize : witSize + baseSize); + } + + private void EstimateScriptSigSize(ICoin coin, ref int witSize, ref int baseSize) + { + if (coin is IColoredCoin) + coin = ((IColoredCoin)coin).Bearer; + + if (coin is ScriptCoin scriptCoin) + { + Script p2sh = scriptCoin.GetP2SHRedeem(); + if (p2sh != null) + { + coin = new Coin(scriptCoin.Outpoint, new TxOut(scriptCoin.Amount, p2sh)); + baseSize += new Script(Op.GetPushOp(p2sh.ToBytes(true))).Length; + if (scriptCoin.RedeemType == RedeemType.WitnessV0) + { + coin = new ScriptCoinSC(coin, scriptCoin.Redeem); + } + } + + if (scriptCoin.RedeemType == RedeemType.WitnessV0) + { + witSize += new Script(Op.GetPushOp(scriptCoin.Redeem.ToBytes(true))).Length; + } + } + + Script scriptPubkey = coin.GetScriptCode(this.Network); + int scriptSigSize = -1; + foreach (BuilderExtension extension in this.Extensions) + { + if (extension.CanEstimateScriptSigSize(this.Network, scriptPubkey)) + { + scriptSigSize = extension.EstimateScriptSigSize(this.Network, scriptPubkey); + break; + } + } + + if (scriptSigSize == -1) + scriptSigSize += coin.TxOut.ScriptPubKey.Length; //Using heuristic to approximate size of unknown scriptPubKey + + if (coin.GetHashVersion(this.Network) == HashVersion.Witness) + witSize += scriptSigSize + 1; //Account for the push + if (coin.GetHashVersion(this.Network) == HashVersion.Original) + baseSize += scriptSigSize; + } + + /// + /// Estimate fees of the built transaction + /// + /// Fee rate + /// + public Money EstimateFees(FeeRate feeRate) + { + if (feeRate == null) + throw new ArgumentNullException("feeRate"); + + int builderCount = this.CurrentGroup.Builders.Count; + Money feeSent = Money.Zero; + try + { + while (true) + { + Transaction tx = BuildTransaction(false); + Money shouldSend = EstimateFees(tx, feeRate); + Money delta = shouldSend - feeSent; + if (delta <= Money.Zero) + break; + SendFees(delta); + feeSent += delta; + } + } + finally + { + while (this.CurrentGroup.Builders.Count != builderCount) + { + this.CurrentGroup.Builders.RemoveAt(this.CurrentGroup.Builders.Count - 1); + } + this._TotalFee -= feeSent; + } + return feeSent; + } + + /// + /// Estimate fees of an unsigned transaction + /// + /// + /// Fee rate + /// + public Money EstimateFees(Transaction tx, FeeRate feeRate) + { + if (tx == null) + throw new ArgumentNullException("tx"); + if (feeRate == null) + throw new ArgumentNullException("feeRate"); + + int estimation = EstimateSize(tx, true); + return feeRate.GetFee(estimation); + } + + private void Sign(TransactionSigningContext ctx, ICoin coin, IndexedTxIn txIn) + { + TxIn input = txIn.TxIn; + if (coin is StealthCoin) + { + var stealthCoin = (StealthCoin)coin; + Key scanKey = FindKey(ctx, stealthCoin.Address.ScanPubKey.ScriptPubKey); + if (scanKey == null) + throw new KeyNotFoundException("Scan key for decrypting StealthCoin not found"); + Key[] spendKeys = stealthCoin.Address.SpendPubKeys.Select(p => FindKey(ctx, p.ScriptPubKey)).Where(p => p != null).ToArray(); + ctx.AdditionalKeys.AddRange(stealthCoin.Uncover(spendKeys, scanKey)); + var normalCoin = new Coin(coin.Outpoint, coin.TxOut); + if (stealthCoin.Redeem != null) + normalCoin = normalCoin.ToScriptCoin(stealthCoin.Redeem); + coin = normalCoin; + } + Script scriptSig = CreateScriptSig(ctx, coin, txIn); + if (scriptSig == null) + return; + var scriptCoin = coin as ScriptCoin; + + Script signatures = null; + if (coin.GetHashVersion(this.Network) == HashVersion.Witness) + { + signatures = txIn.WitScript; + if (scriptCoin != null) + { + if (scriptCoin.IsP2SH) + txIn.ScriptSig = Script.Empty; + if (scriptCoin.RedeemType == RedeemType.WitnessV0) + signatures = RemoveRedeem(signatures); + } + } + else + { + signatures = txIn.ScriptSig; + if (scriptCoin != null && scriptCoin.RedeemType == RedeemType.P2SH) + signatures = RemoveRedeem(signatures); + } + + + signatures = CombineScriptSigs(coin, scriptSig, signatures); + + if (coin.GetHashVersion(this.Network) == HashVersion.Witness) + { + txIn.WitScript = signatures; + if (scriptCoin != null) + { + if (scriptCoin.IsP2SH) + txIn.ScriptSig = new Script(Op.GetPushOp(scriptCoin.GetP2SHRedeem().ToBytes(true))); + if (scriptCoin.RedeemType == RedeemType.WitnessV0) + txIn.WitScript = txIn.WitScript + new WitScript(Op.GetPushOp(scriptCoin.Redeem.ToBytes(true))); + } + } + else + { + txIn.ScriptSig = signatures; + if (scriptCoin != null && scriptCoin.RedeemType == RedeemType.P2SH) + { + txIn.ScriptSig = input.ScriptSig + Op.GetPushOp(scriptCoin.GetP2SHRedeem().ToBytes(true)); + } + } + } + + private static Script RemoveRedeem(Script script) + { + if (script == Script.Empty) + return script; + Op[] ops = script.ToOps().ToArray(); + return new Script(ops.Take(ops.Length - 1)); + } + + private Script CombineScriptSigs(ICoin coin, Script a, Script b) + { + Script scriptPubkey = coin.GetScriptCode(this.Network); + if (Script.IsNullOrEmpty(a)) + return b ?? Script.Empty; + if (Script.IsNullOrEmpty(b)) + return a ?? Script.Empty; + + foreach (BuilderExtension extension in this.Extensions) + { + if (extension.CanCombineScriptSig(this.Network, scriptPubkey, a, b)) + { + return extension.CombineScriptSig(this.Network, scriptPubkey, a, b); + } + } + return a.Length > b.Length ? a : b; //Heurestic + } + + private Script CreateScriptSig(TransactionSigningContext ctx, ICoin coin, IndexedTxIn txIn) + { + Script scriptPubKey = coin.GetScriptCode(this.Network); + var keyRepo = new TransactionBuilderKeyRepository(this, ctx); + var signer = new TransactionBuilderSigner(this, coin, ctx.SigHash, txIn); + + var signer2 = new KnownSignatureSigner(this, this._KnownSignatures, coin, ctx.SigHash, txIn); + + foreach (BuilderExtension extension in this.Extensions) + { + if (extension.CanGenerateScriptSig(this.Network, scriptPubKey)) + { + Script scriptSig1 = extension.GenerateScriptSig(this.Network, scriptPubKey, keyRepo, signer); + Script scriptSig2 = extension.GenerateScriptSig(this.Network, scriptPubKey, signer2, signer2); + if (scriptSig2 != null) + { + scriptSig2 = signer2.ReplaceDummyKeys(scriptSig2); + } + if (scriptSig1 != null && scriptSig2 != null && extension.CanCombineScriptSig(this.Network, scriptPubKey, scriptSig1, scriptSig2)) + { + Script combined = extension.CombineScriptSig(this.Network, scriptPubKey, scriptSig1, scriptSig2); + return combined; + } + return scriptSig1 ?? scriptSig2; + } + } + + throw new NotSupportedException("Unsupported scriptPubKey"); + } + + private List> _KnownSignatures = new List>(); + + private Key FindKey(TransactionSigningContext ctx, Script scriptPubKey) + { + Key key = this._Keys + .Concat(ctx.AdditionalKeys) + .FirstOrDefault(k => IsCompatibleKey(k.PubKey, scriptPubKey)); + if (key == null && this.KeyFinder != null) + { + key = this.KeyFinder(scriptPubKey); + } + return key; + } + + private static bool IsCompatibleKey(PubKey k, Script scriptPubKey) + { + return k.ScriptPubKey == scriptPubKey || //P2PK + k.Hash.ScriptPubKey == scriptPubKey || //P2PKH + k.ScriptPubKey.Hash.ScriptPubKey == scriptPubKey || //P2PK P2SH + k.Hash.ScriptPubKey.Hash.ScriptPubKey == scriptPubKey; //P2PKH P2SH + } + + /// + /// Create a new participant in the transaction with its own set of coins and keys + /// + /// + public TransactionBuilderSC Then() + { + this._CurrentGroup = null; + return this; + } + + /// + /// Switch to another participant in the transaction, or create a new one if it is not found. + /// + /// + public TransactionBuilderSC Then(string groupName) + { + BuilderGroup group = this._BuilderGroups.FirstOrDefault(g => g.Name == groupName); + if (group == null) + { + group = new BuilderGroup(this); + this._BuilderGroups.Add(group); + group.Name = groupName; + } + + this._CurrentGroup = group; + return this; + } + + /// + /// Specify the amount of money to cover txouts, if not specified all txout will be covered + /// + /// + /// + public TransactionBuilderSC CoverOnly(Money amount) + { + this.CurrentGroup.CoverOnly = amount; + return this; + } + + private Transaction _CompletedTransaction; + + /// + /// Allows to keep building on the top of a partially built transaction + /// + /// Transaction to complete + /// + public TransactionBuilderSC ContinueToBuild(Transaction transaction) + { + if (this._CompletedTransaction != null) + throw new InvalidOperationException("Transaction to complete already set"); + + this._CompletedTransaction = this.Network.CreateTransaction(transaction.ToHex()); + + return this; + } + + /// + /// Will cover the remaining amount of TxOut of a partially built transaction (to call after ContinueToBuild) + /// + /// + public TransactionBuilderSC CoverTheRest() + { + if (this._CompletedTransaction == null) + throw new InvalidOperationException("A partially built transaction should be specified by calling ContinueToBuild"); + + Money spent = this._CompletedTransaction.Inputs.AsIndexedInputs().Select(txin => + { + ICoin c = FindCoin(txin.PrevOut); + if (c == null) + throw CoinNotFound(txin); + if (!(c is Coin)) + return null; + return (Coin)c; + }) + .Where(c => c != null) + .Select(c => c.Amount) + .Sum(); + + Money toComplete = this._CompletedTransaction.TotalOut - spent; + this.CurrentGroup.Builders.Add(ctx => + { + if (toComplete < Money.Zero) + return Money.Zero; + return toComplete; + }); + return this; + } + + public TransactionBuilderSC AddCoins(Transaction transaction) + { + uint256 txId = transaction.GetHash(); + AddCoins(transaction.Outputs.Select((o, i) => new Coin(txId, (uint)i, o.Value, o.ScriptPubKey)).ToArray()); + return this; + } + + private Dictionary _ScriptPubKeyToRedeem = new Dictionary(); + public TransactionBuilderSC AddKnownRedeems(params Script[] knownRedeems) + { + foreach (Script redeem in knownRedeems) + { + this._ScriptPubKeyToRedeem.AddOrReplace(redeem.WitHash.ScriptPubKey.Hash.ScriptPubKey, redeem); //Might be P2SH(PWSH) + this._ScriptPubKeyToRedeem.AddOrReplace(redeem.Hash.ScriptPubKey, redeem); //Might be P2SH + this._ScriptPubKeyToRedeem.AddOrReplace(redeem.WitHash.ScriptPubKey, redeem); //Might be PWSH + } + return this; + } + + public Transaction CombineSignatures(params Transaction[] transactions) + { + // Necessary because we can't use params and default parameters together + return this.CombineSignatures(false, transactions); + } + + /// + /// Combine transactions by merging their signatures. + /// + /// + /// An optional optimisation to exit early. If set and the merging of the first input doesn't change the transaction, the method will exit. + /// + /// The transactions to merge signatures for. + /// The merged transaction. + public Transaction CombineSignatures(bool requireFirstSigned, params Transaction[] transactions) + { + if (transactions.Length == 1) + return transactions[0]; + + if (transactions.Length == 0) + return null; + + Transaction tx = this.Network.CreateTransaction(transactions[0].ToHex()); + for (int i = 1; i < transactions.Length; i++) + { + Transaction signed = transactions[i]; + tx = this.CombineSignaturesCore(tx, signed, requireFirstSigned); + } + return tx; + } + + private readonly List _Extensions = new List(); + public List Extensions + { + get + { + return this._Extensions; + } + } + /// + /// Combine multiple transactions into one, merging signatures for all of their inputs. + /// + /// First transaction to sign. + /// Second transaction to sign. + /// + /// An optional optimisation to exit early. If set and the merging of the first input doesn't change the transaction, the method will exit. + /// + /// The combined transaction. + private Transaction CombineSignaturesCore(Transaction signed1, Transaction signed2, bool requireFirstSigned) + { + if (signed1 == null) + return signed2; + + if (signed2 == null) + return signed1; + + Transaction tx = this.Network.CreateTransaction(signed1.ToHex()); + + IndexedTxIn[] signed1Inputs = signed1.Inputs.AsIndexedInputs().ToArray(); + IndexedTxIn[] signed2Inputs = signed2.Inputs.AsIndexedInputs().ToArray(); + + for (int i = 0; i < tx.Inputs.Count; i++) + { + if (i >= signed2.Inputs.Count) + break; + + TxIn txIn = tx.Inputs[i]; + + ICoin coin = FindCoin(txIn.PrevOut); + Script scriptPubKey = coin == null + ? (DeduceScriptPubKey(txIn.ScriptSig) ?? DeduceScriptPubKey(signed2.Inputs[i].ScriptSig)) + : coin.TxOut.ScriptPubKey; + + Money amount = null; + if (coin != null) + amount = coin is IColoredCoin ? ((IColoredCoin)coin).Bearer.Amount : ((Coin)coin).Amount; + ScriptSigs result = Script.CombineSignatures( + this.Network, + scriptPubKey, + new TransactionChecker(tx, i, amount), + GetScriptSigs(signed1Inputs[i]), + GetScriptSigs(signed2Inputs[i])); + IndexedTxIn input = tx.Inputs.AsIndexedInputs().Skip(i).First(); + input.WitScript = result.WitSig; + input.ScriptSig = result.ScriptSig; + + // In certain cases we're expecting every input to be signed. + // If merging the first input doesn't affect the transaction, exit early. + if (i == 0 && requireFirstSigned && tx.GetHash() == signed1.GetHash()) + { + return tx; + } + } + return tx; + } + + private ScriptSigs GetScriptSigs(IndexedTxIn indexedTxIn) + { + return new ScriptSigs() + { + ScriptSig = indexedTxIn.ScriptSig, + WitSig = indexedTxIn.WitScript + }; + } + + private Script DeduceScriptPubKey(Script scriptSig) + { + PayToScriptHashSigParameters p2sh = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.Network, scriptSig); + if (p2sh != null && p2sh.RedeemScript != null) + { + return p2sh.RedeemScript.Hash.ScriptPubKey; + } + foreach (BuilderExtension extension in this.Extensions) + { + if (extension.CanDeduceScriptPubKey(this.Network, scriptSig)) + { + return extension.DeduceScriptPubKey(this.Network, scriptSig); + } + } + return null; + } + } + + public class CoinNotFoundException : KeyNotFoundException + { + public CoinNotFoundException(IndexedTxIn txIn) + : base("No coin matching " + txIn.PrevOut + " was found") + { + this._OutPoint = txIn.PrevOut; + this._InputIndex = txIn.Index; + } + + private readonly OutPoint _OutPoint; + public OutPoint OutPoint + { + get + { + return this._OutPoint; + } + } + + private readonly uint _InputIndex; + public uint InputIndex + { + get + { + return this._InputIndex; + } + } + } +} diff --git a/Src/StratisUnity3d/Assets/Code/Dependencies/TxBuilder.cs.meta b/Src/StratisUnity3d/Assets/Code/Dependencies/TxBuilder.cs.meta new file mode 100644 index 0000000..a2ff7d8 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Dependencies/TxBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27b17c72f44244e479e13cedd4b7afd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/Examples/TestSmartContracts.cs b/Src/StratisUnity3d/Assets/Code/Examples/TestSmartContracts.cs new file mode 100644 index 0000000..2fd397b --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Examples/TestSmartContracts.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using NBitcoin; +using Stratis.Sidechains.Networks; +using Unity3dApi; +using UnityEngine; +using Network = NBitcoin.Network; + +public class TestSmartContracts : MonoBehaviour +{ + async void Start() + { + // First we create network on which we want to interact with smart contracts. + // Cirrus main and test networks support smart contracts. + Network network = new CirrusMain(); + + // API client used to interact with a node. Note that you should run node with '-txindex=1 -addressindex=1 -unityapi_enable=true' arguments. + //Unity3dClient client = new Unity3dClient("http://localhost:44336/"); + Unity3dClient client = new Unity3dClient("https://cirrusapi.stratisplatform.com/"); + + Mnemonic mnemonic = new Mnemonic("legal door leopard fire 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); + + // Deploy DAO contract + //await this.DeployDaoContractAsync(stratisUnityManager).ConfigureAwait(false); + //return; + + ReceiptResponse receipt = await client.ReceiptAsync("95b9c1e8ab28071b750ab61a3647954b0476d75173d91d0c8db0267c4894d1f6").ConfigureAwait(false); + + bool isSuccess = receipt.Success; + string contractAddr = receipt.NewContractAddress; + Debug.Log("Checking contract deployment receipt. Success: " + isSuccess + " ContractAddress: " + contractAddr); + + Debug.Log("Making local contract call."); + + var localCallData = new LocalCallContractRequest() + { + GasPrice = 10000, + Amount = "0", + GasLimit = 250000, + ContractAddress = contractAddr, + MethodName = "MaxVotingDuration", + Sender = stratisUnityManager.GetAddress().ToString(), + Parameters = new List() + }; + LocalExecutionResult localCallResult = await client.LocalCallAsync(localCallData).ConfigureAwait(false); + Debug.Log("MaxVotingDuration: " + localCallResult.Return.ToString()); + + // Make an on-chain smart contract call. + string callId = await stratisUnityManager.SendCallContractTransactionAsync("CNiJEPppjvBf1zAAyjcLD81QbVd8NQ59Bv", + "WhitelistAddress", new string[] {"9#CPokn4GjJHtM7t2b99pdsbLuGd4RbM7pGL"}).ConfigureAwait(false); + + // Call response can be taken from receipt once tx is mined. + + Debug.Log("Api test done"); + } + + private async Task DeployDaoContractAsync(StratisUnityManager stratisUnityManager) + { + // Deploy DAO contract + string constructorParameter = ((int)Stratis.SmartContracts.CLR.Serialization.MethodParameterDataType.UInt).ToString() + "#" + "18900"; + string txId = await stratisUnityManager.SendCreateContractTransactionAsync(WhitelistedContracts.DaoContract.ByteCode, new string[] { constructorParameter }, 0).ConfigureAwait(false); + Debug.Log("Contract deployment tx sent. TxId: " + txId); + } +} diff --git a/Src/StratisUnity3d/Assets/Code/Examples/TestSmartContracts.cs.meta b/Src/StratisUnity3d/Assets/Code/Examples/TestSmartContracts.cs.meta new file mode 100644 index 0000000..7f0dcfe --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/Examples/TestSmartContracts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a723d1e9402f3745b758709a3d521b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts.meta new file mode 100644 index 0000000..47cb137 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5edf8359c7430f64fadd479df46bcd2b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode.meta new file mode 100644 index 0000000..c36a28f --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e2a74562ff9712040abe295dbdf7d7e2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Address.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Address.cs new file mode 100644 index 0000000..7d7c3aa --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Address.cs @@ -0,0 +1,62 @@ +// Decompiled with JetBrains decompiler +// Type: Stratis.SmartContracts.Address +// Assembly: Stratis.SmartContracts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null +// MVID: 313BA83E-A11B-42B1-9AF7-0994F99B5586 +// Assembly location: C:\Users\noescape0\.nuget\packages\stratis.smartcontracts\2.0.0\lib\netcoreapp2.1\Stratis.SmartContracts.dll + +using System; + +namespace Stratis.SmartContracts +{ + public struct Address + { + public static Address Zero; + public const int Width = 20; + private readonly uint pn0; + private readonly uint pn1; + private readonly uint pn2; + private readonly uint pn3; + private readonly uint pn4; + + public Address(Address other) + { + this.pn0 = other.pn0; + this.pn1 = other.pn1; + this.pn2 = other.pn2; + this.pn3 = other.pn3; + this.pn4 = other.pn4; + } + + public Address(uint pn0, uint pn1, uint pn2, uint pn3, uint pn4) + { + this.pn0 = pn0; + this.pn1 = pn1; + this.pn2 = pn2; + this.pn3 = pn3; + this.pn4 = pn4; + } + + public byte[] ToBytes() + { + byte[] numArray = new byte[20]; + Buffer.BlockCopy((Array)BitConverter.GetBytes(this.pn0), 0, (Array)numArray, 0, 4); + Buffer.BlockCopy((Array)BitConverter.GetBytes(this.pn1), 0, (Array)numArray, 4, 4); + Buffer.BlockCopy((Array)BitConverter.GetBytes(this.pn2), 0, (Array)numArray, 8, 4); + Buffer.BlockCopy((Array)BitConverter.GetBytes(this.pn3), 0, (Array)numArray, 12, 4); + Buffer.BlockCopy((Array)BitConverter.GetBytes(this.pn4), 0, (Array)numArray, 16, 4); + return numArray; + } + + public override string ToString() => Address.UIntToHexString(this.pn0) + Address.UIntToHexString(this.pn1) + Address.UIntToHexString(this.pn2) + Address.UIntToHexString(this.pn3) + Address.UIntToHexString(this.pn4); + + private static string UIntToHexString(uint val) => "0123456789ABCDEF"[(int)((val & 240U) >> 4)].ToString() + (object)"0123456789ABCDEF"[(int)val & 15] + (object)"0123456789ABCDEF"[(int)((val & 61440U) >> 12)] + (object)"0123456789ABCDEF"[(int)((val & 3840U) >> 8)] + (object)"0123456789ABCDEF"[(int)((val & 15728640U) >> 20)] + (object)"0123456789ABCDEF"[(int)((val & 983040U) >> 16)] + (object)"0123456789ABCDEF"[(int)((val & 4026531840U) >> 28)] + (object)"0123456789ABCDEF"[(int)((val & 251658240U) >> 24)]; + + public static bool operator ==(Address obj1, Address obj2) => obj1.Equals(obj2); + + public static bool operator !=(Address obj1, Address obj2) => !obj1.Equals(obj2); + + public override bool Equals(object obj) => obj is Address address && this.Equals(address); + + public bool Equals(Address obj) => (1 & ((int)this.pn0 == (int)obj.pn0 ? 1 : 0) & ((int)this.pn1 == (int)obj.pn1 ? 1 : 0) & ((int)this.pn2 == (int)obj.pn2 ? 1 : 0) & ((int)this.pn3 == (int)obj.pn3 ? 1 : 0) & ((int)this.pn4 == (int)obj.pn4 ? 1 : 0)) != 0; + } +} diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Address.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Address.cs.meta new file mode 100644 index 0000000..677bf2c --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Address.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16f66240a4d54fd418369d74091d79ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/AddressExtensions.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/AddressExtensions.cs new file mode 100644 index 0000000..c3cca5a --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/AddressExtensions.cs @@ -0,0 +1,60 @@ +using System; +using NBitcoin; +using Extensions = Stratis.SmartContracts.Core.Extensions; + +namespace Stratis.SmartContracts.CLR +{ + public static class AddressExtensions + { + public static uint160 ToUint160(this Address address) + { + return new uint160(address.ToBytes()); + } + + public static uint160 ToUint160(this string base58Address, Network network) + { + return new uint160(new BitcoinPubKeyAddress(base58Address, network).Hash.ToBytes()); + } + + public static Address ToAddress(this uint160 address) + { + return CreateAddress(address.ToBytes()); + } + + public static Address ToAddress(this string address, Network network) + { + return CreateAddress(address.ToUint160(network).ToBytes()); + } + + public static Address ToAddress(this byte[] bytes) + { + if (bytes.Length != Address.Width) + throw new ArgumentOutOfRangeException(nameof(bytes), "Address must be 20 bytes wide"); + + return CreateAddress(bytes); + } + + public static Address HexToAddress(this string hexString) + { + // uint160 only parses a big-endian hex string + byte[] result = Extensions.HexStringToBytes(hexString); + return CreateAddress(result); + } + + public static string ToBase58Address(this uint160 address, Network network) + { + return new BitcoinPubKeyAddress(new KeyId(address), network).ToString(); + } + + private static Address CreateAddress(byte[] bytes) + { + uint pn0 = BitConverter.ToUInt32(bytes, 0); + uint pn1 = BitConverter.ToUInt32(bytes, 4); + uint pn2 = BitConverter.ToUInt32(bytes, 8); + uint pn3 = BitConverter.ToUInt32(bytes, 12); + uint pn4 = BitConverter.ToUInt32(bytes, 16); + + return new Address(pn0, pn1, pn2, pn3, pn4); + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/AddressExtensions.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/AddressExtensions.cs.meta new file mode 100644 index 0000000..b2c1d6a --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/AddressExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1868c5621b83394b9e6eb33e2188d5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ArrayExtensions.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ArrayExtensions.cs new file mode 100644 index 0000000..45314e1 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ArrayExtensions.cs @@ -0,0 +1,20 @@ +using System; + +namespace Stratis.SmartContracts.CLR +{ + public static class ArrayExtensions + { + public static T[] Slice(this T[] arr, uint start, uint length) + { + if (start + length > arr.Length) + { + throw new ArgumentOutOfRangeException("Array is not long enough"); + } + + var result = new T[length]; + Array.Copy(arr, start, result, 0, length); + + return result; + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ArrayExtensions.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ArrayExtensions.cs.meta new file mode 100644 index 0000000..40803eb --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ArrayExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b5f7eafd795d9344979738c50445363 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/CallDataSerializer.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/CallDataSerializer.cs new file mode 100644 index 0000000..fb8a051 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/CallDataSerializer.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NBitcoin; +using Nethereum.RLP; +using Stratis.SmartContracts.CLR.Serialization; + +namespace Stratis.SmartContracts.CLR +{ + public class CallDataSerializer + { + public const int OpcodeSize = sizeof(byte); + public const int VmVersionSize = sizeof(int); + public const int GasPriceSize = sizeof(ulong); + public const int GasLimitSize = sizeof(ulong); + public const int AddressSize = 20; + public const int PrefixSize = OpcodeSize + VmVersionSize + GasPriceSize + GasLimitSize; + public const int CallContractPrefixSize = PrefixSize + AddressSize; + + private readonly IMethodParameterSerializer methodParamSerializer; + private readonly IContractPrimitiveSerializer primitiveSerializer; + + public CallDataSerializer(IContractPrimitiveSerializer primitiveSerializer) + { + this.primitiveSerializer = primitiveSerializer; + this.methodParamSerializer = new MethodParameterByteSerializer(primitiveSerializer); + } + + public ContractTxData Deserialize(byte[] smartContractBytes) + { + byte type = smartContractBytes[0]; + byte[] vmVersionBytes = smartContractBytes.Slice(OpcodeSize, VmVersionSize); + byte[] gasPriceBytes = smartContractBytes.Slice(OpcodeSize + VmVersionSize, GasPriceSize); + byte[] gasLimitBytes = smartContractBytes.Slice(OpcodeSize + VmVersionSize + GasPriceSize, GasLimitSize); + + int vmVersion = this.primitiveSerializer.Deserialize(vmVersionBytes); + ulong gasPrice = this.primitiveSerializer.Deserialize(gasPriceBytes); + var gasLimit = (RuntimeObserver.Gas)this.primitiveSerializer.Deserialize(gasLimitBytes); + + return IsCallContract(type) + ? this.SerializeCallContract(smartContractBytes, vmVersion, gasPrice, gasLimit) + : this.SerializeCreateContract(smartContractBytes, vmVersion, gasPrice, gasLimit); + } + + protected virtual ContractTxData SerializeCreateContract(byte[] smartContractBytes, int vmVersion, ulong gasPrice, RuntimeObserver.Gas gasLimit) + { + byte[] remaining = smartContractBytes.Slice(PrefixSize, (uint)(smartContractBytes.Length - PrefixSize)); + + IList decodedParams = RLPDecode(remaining); + + var contractExecutionCode = this.primitiveSerializer.Deserialize(decodedParams[0]); + object[] methodParameters = this.DeserializeMethodParameters(decodedParams[1]); + string[] signatures = (decodedParams.Count > 2) ? this.DeserializeSignatures(decodedParams[2]) : null; + + var callData = new ContractTxData(vmVersion, gasPrice, gasLimit, contractExecutionCode, methodParameters, signatures); + return callData; + } + + public ContractTxData SerializeCallContract(byte[] smartContractBytes, int vmVersion, ulong gasPrice, RuntimeObserver.Gas gasLimit) + { + byte[] contractAddressBytes = smartContractBytes.Slice(PrefixSize, AddressSize); + var contractAddress = new uint160(contractAddressBytes); + + byte[] remaining = smartContractBytes.Slice(CallContractPrefixSize, + (uint)(smartContractBytes.Length - CallContractPrefixSize)); + + IList decodedParams = RLPDecode(remaining); + + string methodName = this.primitiveSerializer.Deserialize(decodedParams[0]); + object[] methodParameters = this.DeserializeMethodParameters(decodedParams[1]); + string[] signatures = (decodedParams.Count > 2) ? this.DeserializeSignatures(decodedParams[2]) : null; + + var callData = new ContractTxData(vmVersion, gasPrice, gasLimit, contractAddress, methodName, methodParameters, signatures); + return callData; + } + + protected static IList RLPDecode(byte[] remaining) + { + RLPCollection innerList = (RLPCollection)RLP.Decode(remaining); + + return innerList.Select(x => x.RLPData).ToList(); + } + + public byte[] Serialize(ContractTxData contractTxData) + { + return IsCallContract(contractTxData.OpCodeType) + ? this.SerializeCallContract(contractTxData) + : this.SerializeCreateContract(contractTxData); + } + + private byte[] SerializeCreateContract(ContractTxData contractTxData) + { + var rlpBytes = new List(); + + rlpBytes.Add(contractTxData.ContractExecutionCode); + + this.AddMethodParams(rlpBytes, contractTxData.MethodParameters); + if (contractTxData.Signatures != null) + this.AddSignatures(rlpBytes, contractTxData.Signatures); + + byte[] encoded = RLP.EncodeList(rlpBytes.Select(RLP.EncodeElement).ToArray()); + + var bytes = new byte[PrefixSize + encoded.Length]; + + this.SerializePrefix(bytes, contractTxData); + + encoded.CopyTo(bytes, PrefixSize); + + return bytes; + } + + private byte[] SerializeCallContract(ContractTxData contractTxData) + { + var rlpBytes = new List(); + + rlpBytes.Add(this.primitiveSerializer.Serialize(contractTxData.MethodName)); + + this.AddMethodParams(rlpBytes, contractTxData.MethodParameters); + if (contractTxData.Signatures != null) + this.AddSignatures(rlpBytes, contractTxData.Signatures); + + byte[] encoded = RLP.EncodeList(rlpBytes.Select(RLP.EncodeElement).ToArray()); + + var bytes = new byte[CallContractPrefixSize + encoded.Length]; + + this.SerializePrefix(bytes, contractTxData); + + contractTxData.ContractAddress.ToBytes().CopyTo(bytes, PrefixSize); + + encoded.CopyTo(bytes, CallContractPrefixSize); + + return bytes; + } + + protected void SerializePrefix(byte[] bytes, ContractTxData contractTxData) + { + byte[] vmVersion = this.primitiveSerializer.Serialize(contractTxData.VmVersion); + byte[] gasPrice = this.primitiveSerializer.Serialize(contractTxData.GasPrice); + byte[] gasLimit = this.primitiveSerializer.Serialize(contractTxData.GasLimit.Value); + bytes[0] = contractTxData.OpCodeType; + vmVersion.CopyTo(bytes, OpcodeSize); + gasPrice.CopyTo(bytes, OpcodeSize + VmVersionSize); + gasLimit.CopyTo(bytes, OpcodeSize + VmVersionSize + GasPriceSize); + } + + protected void AddMethodParams(List rlpBytes, object[] methodParameters) + { + if (methodParameters != null && methodParameters.Any()) + { + rlpBytes.Add(this.SerializeMethodParameters(methodParameters)); + } + else + { + rlpBytes.Add(new byte[0]); + } + } + + /// + /// Adds the passed signatures to the passed list of byte arrays. + /// + /// The list of byte arrays to add the signatures to. + /// The signatures as a base 64 encoded byte array. See + protected void AddSignatures(List rlpBytes, string[] signatures) + { + rlpBytes.Add(this.SerializeSignatures(signatures)); + } + + protected static bool IsCallContract(byte type) + { + return type == (byte)ScOpcodeType.OP_CALLCONTRACT; + } + + protected byte[] SerializeMethodParameters(object[] objects) + { + return this.methodParamSerializer.Serialize(objects); + } + + protected object[] DeserializeMethodParameters(byte[] methodParametersRaw) + { + object[] methodParameters = null; + + if (methodParametersRaw != null && methodParametersRaw.Length > 0) + methodParameters = this.methodParamSerializer.Deserialize(methodParametersRaw); + + return methodParameters; + } + + /// + /// Serializes the signatures. + /// + /// Signatures passed as an array of base 64 encoded byte arrays. + /// A byte array containing the decoded signatures, where each signature is prefixed by its length. + protected byte[] SerializeSignatures(string[] signatures) + { + byte[][] signaturesRaw = new byte[signatures.Length][]; + int totalBytes = 0; + for (int i = 0; i < signatures.Length; i++) + { + signaturesRaw[i] = Convert.FromBase64String(signatures[i]); + totalBytes += signaturesRaw[i].Length + 1; + } + + var res = new byte[totalBytes]; + totalBytes = 0; + for (int i = 0; i < signatures.Length; i++) + { + res[totalBytes] = (byte)signaturesRaw[i].Length; + signaturesRaw[i].CopyTo(res, totalBytes + 1); + totalBytes += signaturesRaw[i].Length + 1; + } + + return res; + } + + /// + /// Deserializes signatures. + /// + /// A byte array containing the decoded signatures, where each signature is prefixed by its length. + /// Signatures as an array of base 64 encoded byte arrays. + protected string[] DeserializeSignatures(byte[] signaturesRaw) + { + if (signaturesRaw == null || signaturesRaw.Length == 0) + return new string[0]; + + var signatures = new List(); + + for (int i = 0; i < signaturesRaw.Length; i++) + { + int length = signaturesRaw[i]; + var buffer = new byte[length]; + Array.Copy(signaturesRaw, i + 1, buffer, 0, length); + i += length; + signatures.Add(Convert.ToBase64String(buffer)); + } + + return signatures.ToArray(); + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/CallDataSerializer.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/CallDataSerializer.cs.meta new file mode 100644 index 0000000..c28703a --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/CallDataSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b5b4f954972827241ac92500a492f74c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractPrimitiveSerializer.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractPrimitiveSerializer.cs new file mode 100644 index 0000000..e5a29bc --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractPrimitiveSerializer.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using NBitcoin; +using Nethereum.RLP; + +namespace Stratis.SmartContracts.CLR.Serialization +{ + /// + /// This class serializes and deserializes specific data types + /// when persisting items inside a smart contract. + /// + public class ContractPrimitiveSerializer : IContractPrimitiveSerializer + { + private readonly Network network; + + public ContractPrimitiveSerializer(Network network) + { + this.network = network; + } + + public byte[] Serialize(object o) + { + return o switch + { + null => null, + byte[] bytes => bytes, + Array array => Serialize(array), + byte b1 => new byte[] { b1 }, + char c => Serialize(c), + Address address => Serialize(address), + bool b => Serialize(b), + int i => Serialize(i), + long l => Serialize(l), + UInt128 u => Serialize(u), + UInt256 u => Serialize(u), + uint u => Serialize(u), + ulong u => Serialize(u), + string s => Serialize(s), + _ when o.GetType().IsValueType => SerializeStruct(o), + _ => throw new Exception(string.Format("{0} is not supported.", o.GetType().Name)) + }; + } + + #region Primitive serialization + + private byte[] Serialize(Address address) + { + return address.ToBytes(); + } + + private byte[] Serialize(bool b) + { + return BitConverter.GetBytes(b); + } + + private byte[] Serialize(int i) + { + return BitConverter.GetBytes(i); + } + + private byte[] Serialize(long l) + { + return BitConverter.GetBytes(l); + } + + private byte[] Serialize(uint u) + { + return BitConverter.GetBytes(u); + } + + private byte[] Serialize(ulong ul) + { + return BitConverter.GetBytes(ul); + } + + private byte[] Serialize(UInt128 u128) + { + return u128.ToBytes(); + } + + private byte[] Serialize(UInt256 u256) + { + return u256.ToBytes(); + } + + private byte[] Serialize(char c) + { + return BitConverter.GetBytes(c); + } + + private byte[] Serialize(string s) + { + return Encoding.UTF8.GetBytes(s); + } + + #endregion + + private byte[] SerializeStruct(object o) + { + List toEncode = new List(); + + foreach (FieldInfo field in o.GetType().GetFields()) + { + object value = field.GetValue(o); + byte[] serialized = this.Serialize(value); + toEncode.Add(RLP.EncodeElement(serialized)); + } + + return RLP.EncodeList(toEncode.ToArray()); + } + + private byte[] Serialize(Array array) + { + // Edge case, serializing nonsensical + if (array is byte[] a) + return a; + + List toEncode = new List(); + + for (int i = 0; i < array.Length; i++) + { + object value = array.GetValue(i); + byte[] serialized = this.Serialize(value); + toEncode.Add(RLP.EncodeElement(serialized)); + } + + return RLP.EncodeList(toEncode.ToArray()); + } + + public T Deserialize(byte[] stream) + { + object deserialized = this.Deserialize(typeof(T), stream); + + return (T)deserialized; + } + + public object Deserialize(Type type, byte[] stream) + { + if (stream == null || stream.Length == 0) + return null; + + return Type.GetTypeCode(type) switch + { + TypeCode.Byte => stream[0], + TypeCode.Char => ToChar(stream), + TypeCode.Boolean => ToBool(stream), + TypeCode.Int32 => ToInt32(stream), + TypeCode.Int64 => ToInt64(stream), + TypeCode.String => ToString(stream), + TypeCode.UInt32 => ToUInt32(stream), + TypeCode.UInt64 => ToUInt64(stream), + _ when type == typeof(byte[]) => stream, + _ when type == typeof(UInt128) => ToUInt128(stream), + _ when type == typeof(UInt256) => ToUInt256(stream), + _ when type == typeof(Address) => ToAddress(stream), + _ when type.IsArray => DeserializeArray(type.GetElementType(), stream), + _ when type.IsValueType => DeserializeStruct(type, stream), + _ => throw new Exception(string.Format("{0} is not supported.", type.Name)), + }; + } + + public Address ToAddress(string address) + { + return address.ToAddress(this.network); + } + + #region Primitive Deserialization + + private bool ToBool(byte[] val) + { + return BitConverter.ToBoolean(val, 0); + } + + private Address ToAddress(byte[] val) + { + return val.ToAddress(); + } + + private int ToInt32(byte[] val) + { + return BitConverter.ToInt32(val, 0); + } + + private uint ToUInt32(byte[] val) + { + return BitConverter.ToUInt32(val, 0); + } + + private long ToInt64(byte[] val) + { + return BitConverter.ToInt64(val, 0); + } + + private ulong ToUInt64(byte[] val) + { + return BitConverter.ToUInt64(val, 0); + } + + private UInt128 ToUInt128(byte[] val) + { + return new UInt128(val); + } + + private UInt256 ToUInt256(byte[] val) + { + return new UInt256(val); + } + + private char ToChar(byte[] val) + { + return BitConverter.ToChar(val, 0); + } + + private string ToString(byte[] val) + { + return Encoding.UTF8.GetString(val); + } + + #endregion + + private object DeserializeStruct(Type type, byte[] bytes) + { + RLPCollection collection = (RLPCollection)RLP.Decode(bytes); + + object ret = Activator.CreateInstance(type); + + FieldInfo[] fields = type.GetFields(); + + for (int i = 0; i < fields.Length; i++) + { + byte[] fieldBytes = collection[i].RLPData; + fields[i].SetValue(ret, this.Deserialize(fields[i].FieldType, fieldBytes)); + } + + return ret; + } + + private object DeserializeArray(Type elementType, byte[] bytes) + { + // Edge case, serializing nonsensical + if (elementType == typeof(byte)) + return bytes; + + RLPCollection collection = (RLPCollection)RLP.Decode(bytes); + + Array ret = Array.CreateInstance(elementType, collection.Count); + + for (int i = 0; i < collection.Count; i++) + { + ret.SetValue(this.Deserialize(elementType, collection[i].RLPData), i); + } + + return ret; + } + } +} diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractPrimitiveSerializer.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractPrimitiveSerializer.cs.meta new file mode 100644 index 0000000..b195e19 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractPrimitiveSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 454a6e007631a0849b57a61bd7e49914 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractTxData.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractTxData.cs new file mode 100644 index 0000000..2e01144 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractTxData.cs @@ -0,0 +1,86 @@ +using NBitcoin; + +namespace Stratis.SmartContracts.CLR +{ + /// + /// Fields that are serialized and sent as data with a smart contract transaction + /// + public class ContractTxData + { + /// + /// Creates a ContractTxData object for a method invocation + /// + public ContractTxData(int vmVersion, ulong gasPrice, RuntimeObserver.Gas gasLimit, uint160 contractAddress, + string method, object[] methodParameters = null, string[] signatures = null) + { + this.OpCodeType = (byte)ScOpcodeType.OP_CALLCONTRACT; + this.VmVersion = vmVersion; + this.GasPrice = gasPrice; + this.GasLimit = gasLimit; + this.ContractAddress = contractAddress; + this.MethodName = method; + this.MethodParameters = methodParameters; + this.ContractExecutionCode = new byte[0]; + this.Signatures = signatures; + } + + /// + /// Creates a ContractTxData for contract creation + /// + public ContractTxData(int vmVersion, ulong gasPrice, RuntimeObserver.Gas gasLimit, byte[] code, + object[] methodParameters = null, string[] signatures = null) + { + this.OpCodeType = (byte)ScOpcodeType.OP_CREATECONTRACT; + this.VmVersion = vmVersion; + this.GasPrice = gasPrice; + this.GasLimit = gasLimit; + this.ContractExecutionCode = code; + this.MethodName = ""; + this.MethodParameters = methodParameters; + this.ContractAddress = uint160.Zero; + this.Signatures = signatures; + } + + /// The method name of the contract that will be executed. + public string MethodName { get; } + + public object[] MethodParameters { get; } + + public uint160 ContractAddress { get; } + + /// The maximum amount of gas units that can spent to execute this contract. + public RuntimeObserver.Gas GasLimit { get; } + + /// The amount it costs per unit of gas to execute the contract. + public ulong GasPrice { get; } + + /// + /// The virtual machine version we will use to decompile and execute the contract. + /// + public int VmVersion { get; } + + /// Specifies the smart contract operation to be done. + public byte OpCodeType { get; } + + /// The contract code that will be executed. + public byte[] ContractExecutionCode { get; } + + /// The signatures (if any) passed as an array of base 64 encoded byte arrays - as returned by . + public string[] Signatures { get; } + + /// The maximum cost (in satoshi) the contract can spend. + public ulong GasCostBudget + { + get + { + checked + { + return this.GasPrice * this.GasLimit; + } + } + } + + /// Whether this data represents a contract creation. + public bool IsCreateContract => this.OpCodeType == (byte)ScOpcodeType.OP_CREATECONTRACT; + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractTxData.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractTxData.cs.meta new file mode 100644 index 0000000..c97cce7 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/ContractTxData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de0ef193e35b9314e90aa878453e6e73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Extensions.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Extensions.cs new file mode 100644 index 0000000..0228dc7 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Extensions.cs @@ -0,0 +1,41 @@ +using System; +using NBitcoin; + +namespace Stratis.SmartContracts.Core +{ + public static class Extensions + { + public static string ToHexString(this byte[] ba) + { + return BitConverter.ToString(ba).Replace("-", ""); + } + + public static byte[] HexToByteArray(this string hex) + { + string toHex = hex; + + if (hex.StartsWith("0x")) + toHex = hex.Substring(2); + + int numberChars = toHex.Length; + byte[] bytes = new byte[numberChars / 2]; + for (int i = 0; i < numberChars; i += 2) + bytes[i / 2] = Convert.ToByte(toHex.Substring(i, 2), 16); + return bytes; + } + + public static byte[] HexStringToBytes(string val) + { + if (val.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + val = val.Substring(2); + + byte[] ret = new byte[val.Length / 2]; + for (int i = 0; i < val.Length; i = i + 2) + { + string hexChars = val.Substring(i, 2); + ret[i / 2] = byte.Parse(hexChars, System.Globalization.NumberStyles.HexNumber); + } + return ret; + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Extensions.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Extensions.cs.meta new file mode 100644 index 0000000..9fe2311 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Extensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a31db6a5219872b43a0dce4f0ee71a4b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Gas.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Gas.cs new file mode 100644 index 0000000..a740db3 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Gas.cs @@ -0,0 +1,51 @@ +namespace Stratis.SmartContracts.RuntimeObserver +{ + /// + /// Value type representing an amount of Gas. Use this to avoid confusion with many other ulong values. + /// + public struct Gas + { + public Gas(ulong value) + { + this.Value = value; + } + + public static Gas None = (Gas)0; + + public readonly ulong Value; + + /// + /// Values of type ulong must be explicitly cast to Gas to ensure developer's intention. + /// + /// ulong u = 10000; + /// Gas g = (Gas)u; + /// + /// + public static explicit operator Gas(ulong value) + { + return new Gas(value); + } + + public static explicit operator Gas(int value) + { + return new Gas((ulong)value); + } + + /// + /// Ensures we can implicitly treat Gas as an ulong. + /// + /// Gas g = new Gas(10000); + /// ulong u = g; + /// + /// + public static implicit operator ulong(Gas gas) + { + return gas.Value; + } + + public override string ToString() + { + return this.Value.ToString(); + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Gas.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Gas.cs.meta new file mode 100644 index 0000000..f9ef87b --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Gas.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0946bcf11458eb64c94100c986cee790 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IContractPrimitiveSerializer.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IContractPrimitiveSerializer.cs new file mode 100644 index 0000000..cef8f39 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IContractPrimitiveSerializer.cs @@ -0,0 +1,12 @@ +using System; + +namespace Stratis.SmartContracts.CLR.Serialization +{ + public interface IContractPrimitiveSerializer + { + byte[] Serialize(object obj); + T Deserialize(byte[] stream); + object Deserialize(Type type, byte[] stream); + Address ToAddress(string address); + } +} diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IContractPrimitiveSerializer.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IContractPrimitiveSerializer.cs.meta new file mode 100644 index 0000000..637085b --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IContractPrimitiveSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f896bc89355e984d9005cc8c7150105 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IMethodParameterSerializer.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IMethodParameterSerializer.cs new file mode 100644 index 0000000..e11faf8 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IMethodParameterSerializer.cs @@ -0,0 +1,15 @@ +namespace Stratis.SmartContracts.CLR.Serialization +{ + public interface IMethodParameterSerializer + { + /// + /// Serializes an array of method parameter objects to their byte representation. + /// + byte[] Serialize(object[] methodParameters); + + /// + /// Deserializes an encoded array of bytes to parameter objects. + /// + object[] Deserialize(byte[] methodParameters); + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IMethodParameterSerializer.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IMethodParameterSerializer.cs.meta new file mode 100644 index 0000000..9bec4dc --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/IMethodParameterSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a24856efad0734948988442baefbcd7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterByteSerializer.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterByteSerializer.cs new file mode 100644 index 0000000..8733dab --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterByteSerializer.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Nethereum.RLP; + +namespace Stratis.SmartContracts.CLR.Serialization +{ + /// + /// Serializes method parameters using RLP-encoded byte arrays. + /// + public class MethodParameterByteSerializer : IMethodParameterSerializer + { + private readonly IContractPrimitiveSerializer primitiveSerializer; + + public MethodParameterByteSerializer(IContractPrimitiveSerializer primitiveSerializer) + { + this.primitiveSerializer = primitiveSerializer; + } + + public byte[] Serialize(object[] methodParameters) + { + if (methodParameters == null) + throw new ArgumentNullException(nameof(methodParameters)); + + var result = new List(); + + foreach (object param in methodParameters) + { + byte[] encoded = this.Encode(param); + + result.Add(encoded); + } + + return RLP.EncodeList(result.Select(RLP.EncodeElement).ToArray()); + } + + public object[] Deserialize(byte[] bytes) + { + RLPCollection innerList = (RLPCollection)RLP.Decode(bytes); + + IList encodedParamBytes = innerList.Select(x => x.RLPData).ToList(); + + var results = new List(); + + foreach (byte[] encodedParam in encodedParamBytes) + { + object result = this.Decode(encodedParam); + + results.Add(result); + } + + return results.ToArray(); + } + + private byte[] Encode(object o) + { + Prefix prefix = Prefix.ForObject(o); + + byte[] serializedBytes = this.primitiveSerializer.Serialize(o); + + var result = new byte[prefix.Length + serializedBytes.Length]; + + prefix.CopyTo(result); + + serializedBytes.CopyTo(result, prefix.Length); + + return result; + } + + private object Decode(byte[] bytes) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + if (bytes.Length == 0) + throw new ArgumentOutOfRangeException(nameof(bytes)); + + var prefix = new Prefix(bytes[0]); + + byte[] paramBytes = bytes.Skip(prefix.Length).ToArray(); + + return this.primitiveSerializer.Deserialize(prefix.Type, paramBytes); + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterByteSerializer.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterByteSerializer.cs.meta new file mode 100644 index 0000000..c406024 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterByteSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0594326f149b5f643b1bb8907d41edb4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterDataType.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterDataType.cs new file mode 100644 index 0000000..b45b2a3 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterDataType.cs @@ -0,0 +1,18 @@ +namespace Stratis.SmartContracts.CLR.Serialization +{ + public enum MethodParameterDataType + { + Bool = 1, + Byte = 2, + Char = 3, + String = 4, + UInt = 5, + Int = 6, + ULong = 7, + Long = 8, + Address = 9, + ByteArray = 10, + UInt128 = 11, + UInt256 = 12 + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterDataType.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterDataType.cs.meta new file mode 100644 index 0000000..68477a3 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterDataType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9db2dbfe23fc005428a471c2c4064c99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterStringSerializer.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterStringSerializer.cs new file mode 100644 index 0000000..54752ee --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterStringSerializer.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using NBitcoin; +using Stratis.SmartContracts.Core; + +namespace Stratis.SmartContracts.CLR.Serialization +{ + public interface IMethodParameterStringSerializer + { + /// + /// Serializes a single object to its string representation. + /// + string Serialize(object methodParameters); + + /// + /// Serializes an array of method parameter objects to their string representation. + /// + string Serialize(object[] methodParameters); + + /// + /// Deserializes an encoded array of strings to parameter objects. + /// + object[] Deserialize(string[] parameters); + + /// + /// Deserializes an encoded string of parameters to parameter objects. + /// + object[] Deserialize(string parameters); + } + + /// + /// Class that handles method parameter serialization. + /// + public sealed class MethodParameterStringSerializer : IMethodParameterStringSerializer + { + private readonly Network network; + + public MethodParameterStringSerializer(Network network) + { + this.network = network; + } + + /// + public string Serialize(object methodParameter) + { + return this.SerializeObject(methodParameter); + } + + /// + /// Serializes an array of method parameter objects to the bytes of their string-encoded representation. + /// + public string Serialize(object[] methodParameters) + { + var sb = new List(); + + foreach (object obj in methodParameters) + { + sb.Add(this.SerializeObject(obj)); + } + + return this.EscapeAndJoin(sb.ToArray()); + } + + private string SerializeObject(object obj) + { + Prefix prefix = Prefix.ForObject(obj); + + string serialized = Serialize(obj, this.network); + + return string.Format("{0}#{1}", (int)prefix.DataType, serialized); + } + + public static string Serialize(object obj, Network network) + { + MethodParameterDataType primitiveType = GetPrimitiveType(obj); + + // ToString works fine for all of our data types except byte arrays and addresses + string serialized; + + switch (primitiveType) + { + case MethodParameterDataType.ByteArray: + serialized = ((byte[])obj).ToHexString(); + break; + case MethodParameterDataType.Address: + serialized = ((Address)obj).ToUint160().ToBase58Address(network); + break; + default: + serialized = obj.ToString(); + break; + } + + return serialized; + } + + private static MethodParameterDataType GetPrimitiveType(object o) + { + return o switch + { + bool _ => MethodParameterDataType.Bool, + byte _ => MethodParameterDataType.Byte, + byte[] _ => MethodParameterDataType.ByteArray, + char _ => MethodParameterDataType.Char, + string _ => MethodParameterDataType.String, + uint _ => MethodParameterDataType.UInt, + ulong _ => MethodParameterDataType.ULong, + Address _ => MethodParameterDataType.Address, + long _ => MethodParameterDataType.Long, + int _ => MethodParameterDataType.Int, + UInt128 _ => MethodParameterDataType.UInt128, + UInt256 _ => MethodParameterDataType.UInt256, + _ => throw new MethodParameterStringSerializerException(string.Format("{0} is not supported.", o.GetType().Name)) + }; + } + + public object[] Deserialize(string[] parameters) + { + return this.StringToObjects(this.EscapeAndJoin(parameters)); + } + + public object[] Deserialize(string parameters) + { + return this.StringToObjects(parameters); + } + + private object[] StringToObjects(string parameters) + { + string[] split = Regex.Split(parameters, @"(?(); + + foreach (string parameter in split) + { + string parameterType = ""; + string parameterValue = ""; + + try + { + string[] parameterSignature = + Regex.Split(parameter.Replace(@"\|", "|"), @"(? + private string EscapeAndJoin(string[] parameters) + { + IEnumerable escaped = this.EscapePipesAndHashes(parameters); + return string.Join("|", escaped); + } + + /// + /// Escapes any pipes and hashes in the method parameters. + /// + private IEnumerable EscapePipesAndHashes(string[] parameter) + { + IEnumerable processedPipes = parameter.Select(pipeparam => pipeparam = pipeparam.Replace("|", @"\|")); + + IEnumerable processedHashes = processedPipes.Select(hashparam => + { + + // This delegate splits the string by the hash character. + // + // If the split array is longer than 2 then we need to + // reconstruct the parameter by escaping all hashes + // after the first one. + // + // Once this is done, prepend the string with the data type, + // which is an integer representation of MethodParameterDataType, + // as well as a hash, so that it can be split again upon deserialization. + // + // I.e. 3#dcg#5d# will split into 3 / dcg / 5d + // and then dcg / fd will be reconstructed to dcg\\#5d\\# and + // 3# prepended to make 3#dcg\\#5d\\# + + string[] hashes = hashparam.Split('#'); + if (hashes.Length == 2) + return hashparam; + + var reconstructed = new List(); + for (int i = 1; i < hashes.Length; i++) + { + reconstructed.Add(hashes[i]); + } + + string result = string.Join("#", reconstructed).Replace("#", @"\#"); + return hashes[0].Insert(hashes[0].Length, "#" + result); + }); + + return processedHashes; + } + } + + public class MethodParameterStringSerializerException : Exception + { + public MethodParameterStringSerializerException(string message) : base(message) + { + } + + public MethodParameterStringSerializerException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterStringSerializer.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterStringSerializer.cs.meta new file mode 100644 index 0000000..51160d0 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/MethodParameterStringSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9954b1397215e5a41b29014e861c7f48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Prefix.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Prefix.cs new file mode 100644 index 0000000..4d0dfd3 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Prefix.cs @@ -0,0 +1,115 @@ +using System; + +namespace Stratis.SmartContracts.CLR.Serialization +{ + /// + /// The prefix to use when serializing a method parameter. Represents the of parameter being serialized. + /// + public class Prefix + { + public byte Value { get; } + + public Prefix(byte value) + { + this.Value = value; + } + + public Type Type => this.GetType(this.Value); + + public MethodParameterDataType DataType => (MethodParameterDataType)this.Value; + + public int Length { get; } = 1; + + private Type GetType(byte b) + { + switch ((MethodParameterDataType)b) + { + case MethodParameterDataType.Address: + return typeof(Address); + case MethodParameterDataType.Bool: + return typeof(bool); + case MethodParameterDataType.Byte: + return typeof(byte); + case MethodParameterDataType.Char: + return typeof(char); + case MethodParameterDataType.String: + return typeof(string); + case MethodParameterDataType.Int: + return typeof(int); + case MethodParameterDataType.UInt: + return typeof(uint); + case MethodParameterDataType.Long: + return typeof(long); + case MethodParameterDataType.ULong: + return typeof(ulong); + case MethodParameterDataType.ByteArray: + return typeof(byte[]); + case MethodParameterDataType.UInt128: + return typeof(UInt128); + case MethodParameterDataType.UInt256: + return typeof(UInt256); + default: + throw new ArgumentOutOfRangeException(nameof(b), b, "Unsupported type"); + } + } + + public static Prefix ForObject(object o) + { + byte type = (byte)GetPrimitiveType(o.GetType()); + return new Prefix(type); + } + + public static Prefix ForType(Type t) + { + byte type = (byte)GetPrimitiveType(t); + return new Prefix(type); + } + + private static MethodParameterDataType GetPrimitiveType(Type o) + { + if (o == typeof(bool)) + return MethodParameterDataType.Bool; + + if (o == typeof(byte)) + return MethodParameterDataType.Byte; + + if (o == typeof(byte[])) + return MethodParameterDataType.ByteArray; + + if (o == typeof(char)) + return MethodParameterDataType.Char; + + if (o == typeof(string)) + return MethodParameterDataType.String; + + if (o == typeof(uint)) + return MethodParameterDataType.UInt; + + if (o == typeof(ulong)) + return MethodParameterDataType.ULong; + + if (o == typeof(Address)) + return MethodParameterDataType.Address; + + if (o == typeof(long)) + return MethodParameterDataType.Long; + + if (o == typeof(int)) + return MethodParameterDataType.Int; + + if (o == typeof(UInt128)) + return MethodParameterDataType.UInt128; + + if (o == typeof(UInt256)) + return MethodParameterDataType.UInt256; + + // Any other types are not supported. + throw new ArgumentOutOfRangeException(nameof(o), o, string.Format("{0} is not supported.", o.GetType().Name)); + } + + public void CopyTo(byte[] result) + { + result[0] = this.Value; + } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Prefix.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Prefix.cs.meta new file mode 100644 index 0000000..9c506fe --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Prefix.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 39e5391596484564981fd37f80081f3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLP.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLP.cs new file mode 100644 index 0000000..205bcb7 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLP.cs @@ -0,0 +1,295 @@ +// Decompiled with JetBrains decompiler +// Type: Nethereum.RLP.RLP +// Assembly: Nethereum.RLP, Version=3.8.0.0, Culture=neutral, PublicKeyToken=8768a594786aba4e +// MVID: 25BCB5E5-890F-431D-B8DB-9F8F5E3AEA2E +// Assembly location: C:\Users\noescape0\.nuget\packages\nethereum.rlp\3.8.0\lib\netcoreapp3.1\Nethereum.RLP.dll + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Nethereum.RLP +{ + public class RLP + { + private const int SIZE_THRESHOLD = 56; + private const byte OFFSET_SHORT_ITEM = 128; + private const byte OFFSET_LONG_ITEM = 183; + private const byte OFFSET_SHORT_LIST = 192; + private const byte OFFSET_LONG_LIST = 247; + public static readonly byte[] EMPTY_BYTE_ARRAY = new byte[0]; + public static readonly byte[] ZERO_BYTE_ARRAY = new byte[1]; + + public static int ByteArrayToInt(byte[] bytes) + { + if (BitConverter.IsLittleEndian) + Array.Reverse(bytes); + return BitConverter.ToInt32(bytes, 0); + } + + public static IRLPElement Decode(byte[] msgData) + { + RLPCollection rlpCollection = new RLPCollection(); + Nethereum.RLP.RLP.Decode(msgData, 0, 0, msgData.Length, 1, rlpCollection); + return rlpCollection[0]; + } + + public static void Decode( + byte[] msgData, + int level, + int startPosition, + int endPosition, + int levelToIndex, + RLPCollection rlpCollection) + { + if (msgData == null || msgData.Length == 0) + return; + byte[] numArray = new byte[endPosition - startPosition]; + Array.Copy((Array)msgData, startPosition, (Array)numArray, 0, numArray.Length); + try + { + int currentPosition = startPosition; + while (currentPosition < endPosition) + { + if (Nethereum.RLP.RLP.IsListBiggerThan55Bytes(msgData, currentPosition)) + currentPosition = Nethereum.RLP.RLP.ProcessListBiggerThan55Bytes(msgData, level, levelToIndex, rlpCollection, currentPosition); + else if (Nethereum.RLP.RLP.IsListLessThan55Bytes(msgData, currentPosition)) + currentPosition = Nethereum.RLP.RLP.ProcessListLessThan55Bytes(msgData, level, levelToIndex, rlpCollection, currentPosition); + else if (Nethereum.RLP.RLP.IsItemBiggerThan55Bytes(msgData, currentPosition)) + currentPosition = Nethereum.RLP.RLP.ProcessItemBiggerThan55Bytes(msgData, rlpCollection, currentPosition); + else if (Nethereum.RLP.RLP.IsItemLessThan55Bytes(msgData, currentPosition)) + currentPosition = Nethereum.RLP.RLP.ProcessItemLessThan55Bytes(msgData, rlpCollection, currentPosition); + else if (Nethereum.RLP.RLP.IsNullItem(msgData, currentPosition)) + currentPosition = Nethereum.RLP.RLP.ProcessNullItem(rlpCollection, currentPosition); + else if (Nethereum.RLP.RLP.IsSigleByteItem(msgData, currentPosition)) + currentPosition = Nethereum.RLP.RLP.ProcessSingleByteItem(msgData, rlpCollection, currentPosition); + } + } + catch (Exception ex) + { + throw new Exception("Invalid RLP ", ex); + } + } + + private static bool IsListBiggerThan55Bytes(byte[] msgData, int currentPosition) => msgData[currentPosition] > (byte)247; + + private static bool IsListLessThan55Bytes(byte[] msgData, int currentPosition) => msgData[currentPosition] >= (byte)192 && msgData[currentPosition] <= (byte)247; + + private static bool IsItemBiggerThan55Bytes(byte[] msgData, int currentPosition) => msgData[currentPosition] > (byte)183 && msgData[currentPosition] < (byte)192; + + private static bool IsItemLessThan55Bytes(byte[] msgData, int currentPosition) => msgData[currentPosition] > (byte)128 && msgData[currentPosition] <= (byte)183; + + private static bool IsNullItem(byte[] msgData, int currentPosition) => msgData[currentPosition] == (byte)128; + + private static bool IsSigleByteItem(byte[] msgData, int currentPosition) => msgData[currentPosition] < (byte)128; + + private static int ProcessSingleByteItem( + byte[] msgData, + RLPCollection rlpCollection, + int currentPosition) + { + RLPItem rlpItem = new RLPItem(new byte[1] + { + msgData[currentPosition] + }); + rlpCollection.Add((IRLPElement)rlpItem); + ++currentPosition; + return currentPosition; + } + + private static int ProcessNullItem(RLPCollection rlpCollection, int currentPosition) + { + RLPItem rlpItem = new RLPItem(Nethereum.RLP.RLP.EMPTY_BYTE_ARRAY); + rlpCollection.Add((IRLPElement)rlpItem); + ++currentPosition; + return currentPosition; + } + + private static int ProcessItemLessThan55Bytes( + byte[] msgData, + RLPCollection rlpCollection, + int currentPosition) + { + byte num = (byte)((uint)msgData[currentPosition] - 128U); + byte[] rlpData = new byte[(int)num]; + Array.Copy((Array)msgData, currentPosition + 1, (Array)rlpData, 0, (int)num); + byte[] numArray = new byte[2]; + Array.Copy((Array)msgData, currentPosition, (Array)numArray, 0, 2); + RLPItem rlpItem = new RLPItem(rlpData); + rlpCollection.Add((IRLPElement)rlpItem); + currentPosition += 1 + (int)num; + return currentPosition; + } + + private static int ProcessItemBiggerThan55Bytes( + byte[] msgData, + RLPCollection rlpCollection, + int currentPosition) + { + byte num = (byte)((uint)msgData[currentPosition] - 183U); + int length = Nethereum.RLP.RLP.CalculateLength((int)num, msgData, currentPosition); + byte[] rlpData = new byte[length]; + Array.Copy((Array)msgData, currentPosition + (int)num + 1, (Array)rlpData, 0, length); + byte[] numArray = new byte[(int)num + 1]; + Array.Copy((Array)msgData, currentPosition, (Array)numArray, 0, (int)num + 1); + RLPItem rlpItem = new RLPItem(rlpData); + rlpCollection.Add((IRLPElement)rlpItem); + currentPosition += (int)num + length + 1; + return currentPosition; + } + + private static int ProcessListLessThan55Bytes( + byte[] msgData, + int level, + int levelToIndex, + RLPCollection rlpCollection, + int currentPosition) + { + int num = (int)msgData[currentPosition] - 192; + int length = num + 1; + byte[] numArray = new byte[num + 1]; + Array.Copy((Array)msgData, currentPosition, (Array)numArray, 0, length); + RLPCollection rlpCollection1 = new RLPCollection() + { + RLPData = numArray + }; + if (num > 0) + Nethereum.RLP.RLP.Decode(msgData, level + 1, currentPosition + 1, currentPosition + length, levelToIndex, rlpCollection1); + rlpCollection.Add((IRLPElement)rlpCollection1); + currentPosition += length; + return currentPosition; + } + + private static int ProcessListBiggerThan55Bytes( + byte[] msgData, + int level, + int levelToIndex, + RLPCollection rlpCollection, + int currentPosition) + { + byte num = (byte)((uint)msgData[currentPosition] - 247U); + int length1 = Nethereum.RLP.RLP.CalculateLength((int)num, msgData, currentPosition); + int length2 = (int)num + length1 + 1; + byte[] numArray = new byte[length2]; + Array.Copy((Array)msgData, currentPosition, (Array)numArray, 0, length2); + RLPCollection rlpCollection1 = new RLPCollection() + { + RLPData = numArray + }; + Nethereum.RLP.RLP.Decode(msgData, level + 1, currentPosition + (int)num + 1, currentPosition + length2, levelToIndex, rlpCollection1); + rlpCollection.Add((IRLPElement)rlpCollection1); + currentPosition += length2; + return currentPosition; + } + + public static IRLPElement DecodeFirstElement(byte[] msgData, int startPos) + { + RLPCollection rlpCollection = new RLPCollection(); + Nethereum.RLP.RLP.Decode(msgData, 0, startPos, startPos + 1, 1, rlpCollection); + return rlpCollection[0]; + } + + public static byte[] EncodeByte(byte singleByte) => singleByte == (byte)0 ? new byte[1] + { + (byte) 128 + } : (singleByte <= (byte)127 ? new byte[1] + { + singleByte + } : new byte[2] { (byte)129, singleByte }); + + public static byte[] EncodeElement(byte[] srcData) + { + if (Nethereum.RLP.RLP.IsNullOrZeroArray(srcData)) + return new byte[1] { (byte)128 }; + if (Nethereum.RLP.RLP.IsSingleZero(srcData) || srcData.Length == 1 && srcData[0] < (byte)128) + return srcData; + if (srcData.Length < 56) + { + byte num = (byte)(128 + srcData.Length); + byte[] numArray = new byte[srcData.Length + 1]; + Array.Copy((Array)srcData, 0, (Array)numArray, 1, srcData.Length); + numArray[0] = num; + return numArray; + } + int length = srcData.Length; + byte num1 = 0; + for (; length != 0; length >>= 8) + ++num1; + byte[] numArray1 = new byte[(int)num1]; + for (int index = 0; index < (int)num1; ++index) + numArray1[(int)num1 - 1 - index] = (byte)(srcData.Length >> 8 * index); + byte[] numArray2 = new byte[srcData.Length + 1 + (int)num1]; + Array.Copy((Array)srcData, 0, (Array)numArray2, 1 + (int)num1, srcData.Length); + numArray2[0] = (byte)(183U + (uint)num1); + Array.Copy((Array)numArray1, 0, (Array)numArray2, 1, numArray1.Length); + return numArray2; + } + + public static byte[] EncodeElementsAndList(params byte[][] dataItems) => Nethereum.RLP.RLP.EncodeList(((IEnumerable)dataItems).Select(new Func(Nethereum.RLP.RLP.EncodeElement)).ToArray()); + + public static byte[] EncodeList(params byte[][] items) + { + if (items == null) + return new byte[1] { (byte)192 }; + int num1 = 0; + for (int index = 0; index < items.Length; ++index) + num1 += items[index].Length; + byte[] numArray1; + int destinationIndex; + if (num1 < 56) + { + numArray1 = new byte[1 + num1]; + numArray1[0] = (byte)(192 + num1); + destinationIndex = 1; + } + else + { + int num2 = num1; + byte num3 = 0; + for (; num2 != 0; num2 >>= 8) + ++num3; + int num4 = num1; + byte[] numArray2 = new byte[(int)num3]; + for (int index = 0; index < (int)num3; ++index) + numArray2[(int)num3 - 1 - index] = (byte)(num4 >> 8 * index); + numArray1 = new byte[1 + numArray2.Length + num1]; + numArray1[0] = (byte)(247U + (uint)num3); + Array.Copy((Array)numArray2, 0, (Array)numArray1, 1, numArray2.Length); + destinationIndex = numArray2.Length + 1; + } + foreach (byte[] numArray2 in items) + { + Array.Copy((Array)numArray2, 0, (Array)numArray1, destinationIndex, numArray2.Length); + destinationIndex += numArray2.Length; + } + return numArray1; + } + + public static bool IsNullOrZeroArray(byte[] array) => array == null || array.Length == 0; + + public static bool IsSingleZero(byte[] array) => array.Length == 1 && array[0] == (byte)0; + + private static int CalculateLength(int lengthOfLength, byte[] msgData, int pos) + { + byte num1 = (byte)(lengthOfLength - 1); + int num2 = 0; + for (int index = 1; index <= lengthOfLength; ++index) + { + num2 += (int)msgData[pos + index] << 8 * (int)num1; + --num1; + } + return num2; + } + } + + public class RLPItem : IRLPElement + { + private readonly byte[] rlpData; + + public RLPItem(byte[] rlpData) => this.rlpData = rlpData; + + public byte[] RLPData => this.GetRLPData(); + + private byte[] GetRLPData() => this.rlpData.Length != 0 ? this.rlpData : (byte[])null; + } +} diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLP.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLP.cs.meta new file mode 100644 index 0000000..ef62f58 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLP.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a749490b69c91a44b9388b700e3e1de +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLPCollection.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLPCollection.cs new file mode 100644 index 0000000..42b91dd --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLPCollection.cs @@ -0,0 +1,20 @@ +// Decompiled with JetBrains decompiler +// Type: Nethereum.RLP.RLPCollection +// Assembly: Nethereum.RLP, Version=3.8.0.0, Culture=neutral, PublicKeyToken=8768a594786aba4e +// MVID: 25BCB5E5-890F-431D-B8DB-9F8F5E3AEA2E +// Assembly location: C:\Users\noescape0\.nuget\packages\nethereum.rlp\3.8.0\lib\netcoreapp3.1\Nethereum.RLP.dll + +using System.Collections.Generic; + +namespace Nethereum.RLP +{ + public class RLPCollection : List, IRLPElement + { + public byte[] RLPData { get; set; } + } + + public interface IRLPElement + { + byte[] RLPData { get; } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLPCollection.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLPCollection.cs.meta new file mode 100644 index 0000000..34308ec --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/RLPCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6487fabc5a76cc44f8d8d08cbb00f3d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Recipient.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Recipient.cs new file mode 100644 index 0000000..33c18d4 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Recipient.cs @@ -0,0 +1,25 @@ +using NBitcoin; + +namespace Stratis.Bitcoin.Features.Wallet +{ + /// + /// Represents recipients of a payment, used in . + /// + public class Recipient + { + /// + /// The destination script. + /// + public Script ScriptPubKey { get; set; } + + /// + /// The amount that will be sent. + /// + public Money Amount { get; set; } + + /// + /// An indicator if the fee is subtracted from the current recipient. + /// + public bool SubtractFeeFromAmount { get; set; } + } +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Recipient.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Recipient.cs.meta new file mode 100644 index 0000000..7d53738 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/Recipient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8614452f1e2a78f47804e3d821d31485 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt128.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt128.cs new file mode 100644 index 0000000..957e7dc --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt128.cs @@ -0,0 +1,88 @@ +// Decompiled with JetBrains decompiler +// Type: Stratis.SmartContracts.UInt128 +// Assembly: Stratis.SmartContracts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null +// MVID: 313BA83E-A11B-42B1-9AF7-0994F99B5586 +// Assembly location: C:\Users\noescape0\.nuget\packages\stratis.smartcontracts\2.0.0\lib\netcoreapp2.1\Stratis.SmartContracts.dll + +using System; +using System.Numerics; + +namespace Stratis.SmartContracts +{ + public struct UInt128 : IComparable + { + private const int WIDTH = 16; + internal UIntBase value; + + public static UInt128 Zero => (UInt128)0; + + public static UInt128 MinValue => (UInt128)0; + + public static UInt128 MaxValue => new UInt128((BigInteger.One << 128) - (BigInteger)1); + + public UInt128(string hex) => this.value = new UIntBase(16, hex); + + public static UInt128 Parse(string str) => new UInt128(str); + + internal UInt128(BigInteger value) => this.value = new UIntBase(16, value); + + public UInt128(byte[] vch, bool lendian = true) => this.value = new UIntBase(16, vch, lendian); + + public static UInt128 operator >>(UInt128 a, int shift) => new UInt128(a.value.ShiftRight(shift)); + + public static UInt128 operator <<(UInt128 a, int shift) => new UInt128(a.value.ShiftLeft(shift)); + + public static UInt128 operator -(UInt128 a, UInt128 b) => new UInt128(a.value.Subtract(b.value.GetValue())); + + public static UInt128 operator +(UInt128 a, UInt128 b) => new UInt128(a.value.Add(b.value.GetValue())); + + public static UInt128 operator *(UInt128 a, UInt128 b) => new UInt128(a.value.Multiply(b.value.GetValue())); + + public static UInt128 operator /(UInt128 a, UInt128 b) => new UInt128(a.value.Divide(b.value.GetValue())); + + public static UInt128 operator %(UInt128 a, UInt128 b) => new UInt128(a.value.Mod(b.value.GetValue())); + + public UInt128(byte[] vch) + : this(vch, true) + { + } + + public static bool operator <(UInt128 a, UInt128 b) => UIntBase.Comparison(a.value, b.value) < 0; + + public static bool operator >(UInt128 a, UInt128 b) => UIntBase.Comparison(a.value, b.value) > 0; + + public static bool operator <=(UInt128 a, UInt128 b) => UIntBase.Comparison(a.value, b.value) <= 0; + + public static bool operator >=(UInt128 a, UInt128 b) => UIntBase.Comparison(a.value, b.value) >= 0; + + public static bool operator ==(UInt128 a, UInt128 b) => UIntBase.Comparison(a.value, b.value) == 0; + + public static bool operator !=(UInt128 a, UInt128 b) => !(a == b); + + public static implicit operator UInt128(ulong value) => new UInt128((BigInteger)value); + + public static implicit operator UInt128(long value) => new UInt128((BigInteger)value); + + public static implicit operator UInt128(int value) => new UInt128((BigInteger)value); + + public static implicit operator UInt128(uint value) => new UInt128((BigInteger)value); + + public static explicit operator int(UInt128 value) => (int)value.value.GetValue(); + + public static explicit operator uint(UInt128 value) => (uint)value.value.GetValue(); + + public static explicit operator long(UInt128 value) => (long)value.value.GetValue(); + + public static explicit operator ulong(UInt128 value) => (ulong)value.value.GetValue(); + + public byte[] ToBytes() => this.value.ToBytes(); + + public int CompareTo(object b) => this.value.CompareTo((object)((UInt128)b).value); + + public override int GetHashCode() => this.value.GetHashCode(); + + public override bool Equals(object obj) => this.CompareTo(obj) == 0; + + public override string ToString() => this.value.ToString(); + } +} diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt128.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt128.cs.meta new file mode 100644 index 0000000..a1bfa31 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt128.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b3b67f2ffbbe3d4e8756376c4dc1c3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt256.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt256.cs new file mode 100644 index 0000000..769d453 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt256.cs @@ -0,0 +1,97 @@ +// Decompiled with JetBrains decompiler +// Type: Stratis.SmartContracts.UInt256 +// Assembly: Stratis.SmartContracts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null +// MVID: 313BA83E-A11B-42B1-9AF7-0994F99B5586 +// Assembly location: C:\Users\noescape0\.nuget\packages\stratis.smartcontracts\2.0.0\lib\netcoreapp2.1\Stratis.SmartContracts.dll + +using System; +using System.Numerics; + +namespace Stratis.SmartContracts +{ + public struct UInt256 : IComparable + { + private const int WIDTH = 32; + internal UIntBase value; + + public static UInt256 Zero => (UInt256)0; + + public static UInt256 MinValue => (UInt256)0; + + public static UInt256 MaxValue => new UInt256((BigInteger.One << 256) - (BigInteger)1); + + public UInt256(string hex) => this.value = new UIntBase(32, hex); + + public static UInt256 Parse(string str) => new UInt256(str); + + internal UInt256(BigInteger value) => this.value = new UIntBase(32, value); + + public UInt256(byte[] vch, bool lendian = true) => this.value = new UIntBase(32, vch, lendian); + + public static UInt256 operator >>(UInt256 a, int shift) => new UInt256(a.value.ShiftRight(shift)); + + public static UInt256 operator <<(UInt256 a, int shift) => new UInt256(a.value.ShiftLeft(shift)); + + public static UInt256 operator -(UInt256 a, UInt256 b) => new UInt256(a.value.Subtract(b.value.GetValue())); + + public static UInt256 operator +(UInt256 a, UInt256 b) => new UInt256(a.value.Add(b.value.GetValue())); + + public static UInt256 operator *(UInt256 a, UInt256 b) => new UInt256(a.value.Multiply(b.value.GetValue())); + + public static UInt256 operator /(UInt256 a, UInt256 b) => new UInt256(a.value.Divide(b.value.GetValue())); + + public static UInt256 operator %(UInt256 a, UInt256 b) => new UInt256(a.value.Mod(b.value.GetValue())); + + public UInt256(byte[] vch) + : this(vch, true) + { + } + + public static bool operator <(UInt256 a, UInt256 b) => UIntBase.Comparison(a.value, b.value) < 0; + + public static bool operator >(UInt256 a, UInt256 b) => UIntBase.Comparison(a.value, b.value) > 0; + + public static bool operator <=(UInt256 a, UInt256 b) => UIntBase.Comparison(a.value, b.value) <= 0; + + public static bool operator >=(UInt256 a, UInt256 b) => UIntBase.Comparison(a.value, b.value) >= 0; + + public static bool operator ==(UInt256 a, UInt256 b) => UIntBase.Comparison(a.value, b.value) == 0; + + public static bool operator !=(UInt256 a, UInt256 b) => !(a == b); + + public static implicit operator UInt256(ulong value) => new UInt256((BigInteger)value); + + public static implicit operator UInt256(long value) => new UInt256((BigInteger)value); + + public static implicit operator UInt256(int value) => new UInt256((BigInteger)value); + + public static implicit operator UInt256(uint value) => new UInt256((BigInteger)value); + + public static implicit operator UInt256(UInt128 value) + { + byte[] vch = new byte[32]; + value.ToBytes().CopyTo((Array)vch, 0); + return new UInt256(vch); + } + + public static explicit operator int(UInt256 value) => (int)value.value.GetValue(); + + public static explicit operator uint(UInt256 value) => (uint)value.value.GetValue(); + + public static explicit operator long(UInt256 value) => (long)value.value.GetValue(); + + public static explicit operator ulong(UInt256 value) => (ulong)value.value.GetValue(); + + public static explicit operator UInt128(UInt256 value) => new UInt128(value.value.GetValue()); + + public byte[] ToBytes() => this.value.ToBytes(); + + public int CompareTo(object b) => this.value.CompareTo((object)((UInt256)b).value); + + public override int GetHashCode() => this.value.GetHashCode(); + + public override bool Equals(object obj) => this.CompareTo(obj) == 0; + + public override string ToString() => this.value.ToString(); + } +} diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt256.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt256.cs.meta new file mode 100644 index 0000000..a60d2c5 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UInt256.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55cfd3ecb5f05fb4ca65d483a4d70815 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UIntBase.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UIntBase.cs new file mode 100644 index 0000000..3c8c542 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UIntBase.cs @@ -0,0 +1,134 @@ +// Decompiled with JetBrains decompiler +// Type: Stratis.SmartContracts.UIntBase +// Assembly: Stratis.SmartContracts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null +// MVID: 313BA83E-A11B-42B1-9AF7-0994F99B5586 +// Assembly location: C:\Users\noescape0\.nuget\packages\stratis.smartcontracts\2.0.0\lib\netcoreapp2.1\Stratis.SmartContracts.dll + +using System; +using System.Globalization; +using System.Numerics; + +namespace Stratis.SmartContracts +{ + internal struct UIntBase : IComparable + { + private int width; + private BigInteger value; + + public UIntBase(int width) => this.width = (width & 3) == 0 ? width : throw new ArgumentException("The 'width' must be a multiple of 4."); + + public UIntBase(int width, BigInteger value) + : this(width) + => this.SetValue(value); + + public UIntBase(int width, UIntBase value) + : this(width) + => this.SetValue(value.value); + + public UIntBase(int width, ulong b) + : this(width) + => this.SetValue(new BigInteger(b)); + + public UIntBase(int width, byte[] vch, bool lendian = true) + : this(width) + { + if (vch.Length > this.width) + throw new FormatException(string.Format("The byte array should be {0} bytes or less.", (object)this.width)); + this.SetValue(new BigInteger(vch)); + } + + public UIntBase(int width, string str) + : this(width) + { + if (str.StartsWith("0x")) + this.SetValue(BigInteger.Parse("0" + str.Substring(2), NumberStyles.HexNumber)); + else + this.SetValue(BigInteger.Parse(str)); + } + + public UIntBase(int width, uint[] array) + : this(width) + { + int num = this.width / 4; + if (array.Length != num) + throw new FormatException(string.Format("The array length should be {0}.", (object)num)); + byte[] numArray = new byte[this.width]; + for (int index = 0; index < num; ++index) + BitConverter.GetBytes(array[index]).CopyTo((Array)numArray, index * 4); + this.SetValue(new BigInteger(numArray)); + } + + private bool TooBig(byte[] bytes) => bytes.Length > this.width && (bytes.Length != this.width + 1 || bytes[this.width] != (byte)0); + + private void SetValue(BigInteger value) + { + if (value.Sign < 0) + throw new OverflowException("Only positive or zero values are allowed."); + this.value = !this.TooBig(value.ToByteArray()) ? value : throw new OverflowException(); + } + + public BigInteger GetValue() => this.value; + + private uint[] ToUIntArray() + { + byte[] bytes = this.ToBytes(); + int length = this.width / 4; + uint[] numArray = new uint[length]; + for (int index = 0; index < length; ++index) + numArray[index] = BitConverter.ToUInt32(bytes, index * 4); + return numArray; + } + + public byte[] ToBytes(bool lendian = true) + { + byte[] byteArray = this.value.ToByteArray(); + byte[] array = new byte[this.width]; + Array.Copy((Array)byteArray, (Array)array, Math.Min(byteArray.Length, array.Length)); + + if (!lendian) + Array.Reverse(array); + + return array; + } + + internal BigInteger ShiftRight(int shift) => this.value >> shift; + + internal BigInteger ShiftLeft(int shift) => this.value << shift; + + internal BigInteger Add(BigInteger value2) => this.value + value2; + + internal BigInteger Subtract(BigInteger value2) + { + if (this.value.CompareTo(value2) < 0) + throw new OverflowException("Result cannot be negative."); + return this.value - value2; + } + + internal BigInteger Multiply(BigInteger value2) => this.value * value2; + + internal BigInteger Divide(BigInteger value2) => this.value / value2; + + internal BigInteger Mod(BigInteger value2) => this.value % value2; + + public int CompareTo(object b) => this.value.CompareTo(((UIntBase)b).value); + + public static int Comparison(UIntBase a, UIntBase b) => a.CompareTo((object)b); + + public override int GetHashCode() + { + uint[] uintArray = this.ToUIntArray(); + uint num = 0; + for (int index = 0; index < uintArray.Length; ++index) + num ^= uintArray[index]; + return (int)num; + } + + public override bool Equals(object obj) => this.CompareTo(obj) == 0; + + private static string ByteArrayToString(byte[] ba) => BitConverter.ToString(ba).Replace("-", ""); + + public string ToHex() => UIntBase.ByteArrayToString(this.ToBytes(false)).ToLower(); + + public override string ToString() => this.value.ToString(); + } +} diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UIntBase.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UIntBase.cs.meta new file mode 100644 index 0000000..aff8d9f --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/FromNode/UIntBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aeecfd9481bf66b49a4a7fd93aa5db96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/WhitelistedContracts.cs b/Src/StratisUnity3d/Assets/Code/SmartContracts/WhitelistedContracts.cs new file mode 100644 index 0000000..de52f0b --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/WhitelistedContracts.cs @@ -0,0 +1,16 @@ + +public static class WhitelistedContracts +{ + // https://github.com/stratisproject/CirrusSmartContracts/tree/master/Mainnet/DAOContract + public static WhitelistedContract DaoContract = new WhitelistedContract() { Hash = "0ae0598146338decb129978a1370c1500a5306cdb4db1ba32d3e42118ddf0e18", ByteCode = "4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010200753A76920000000000000000E00022200B013000001E00000002000000000000F23D0000002000000040000000000010002000000002000004000000000000000400000000000000006000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000A03D00004F000000000000000000000000000000000000000000000000000000004000000C000000843D00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000F81D000000200000001E000000020000000000000000000000000000200000602E72656C6F6300000C000000004000000002000000200000000000000000000000000000400000420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000D43D00000000000048000000020005002C2800005815000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004602280600000A72010000706F0700000A2A4A02280600000A7201000070036F0800000A2A2E022804000006185C17D72A4602280600000A720D0000706F0900000A2A4A02280600000A720D000070036F0A00000A2A4602280600000A722F0000706F0900000A2A4A02280600000A722F000070036F0A00000A2A4602280600000A72530000706F0900000A2A4A02280600000A7253000070036F0A00000A2A4602280600000A72770000706F0900000A2A4A02280600000A7277000070036F0A00000A2A7202280600000A7295000070038C07000001280B00000A6F0C00000A2A7602280600000A7295000070038C07000001280B00000A046F0D00000A2A7202280600000A72B5000070038C10000001280B00000A6F0E00000A2A7602280600000A72B5000070038C10000001280B00000A046F0F00000A2A7202280600000A72CF000070038C10000001280B00000A6F0900000A2A7602280600000A72CF000070038C10000001280B00000A046F0A00000A2A7202280600000A72E9000070038C10000001280B00000A6F0900000A2A7602280600000A72E9000070038C10000001280B00000A046F0A00000A2A8A02280600000A7201010070038C10000001048C07000001281000000A6F0900000A2A8E02280600000A7201010070038C10000001048C07000001281000000A056F0A00000A2A7202280600000A721B010070038C10000001280B00000A6F0100002B2A7602280600000A721B010070038C10000001280B00000A046F0200002B2A001330040054000000000000000203281300000A0220A893000004FE03723701007020A89300008C10000001280B00000A281400000A0202281500000A6F1600000A28020000060217280B000006020428070000060220A893000028090000062A13300500FB000000010000110228290000060205022806000006360B05022808000006FE052B011672B30100700228060000068C100000010228080000068C10000001281000000A281400000A0E042D03162B070E04281700000A0A020620C8000000FE0216FE017211020070281400000A1203FE15080000021203047D1400000412030E047D150000041203037D13000004120302281500000A6F1600000A7D12000004090B02280A0000060C02080728170000060208056E02281800000A6F1900000AD7280F000006021204FE15040000021204087D070000041204037D060000041204047D0800000412040E047D090000041104280300002B020817D7280B000006082A00133004005000000000000000022829000006020202281500000A6F1600000A280C0000067279020070281400000A020203280E00000602281800000A6F1900000AFE0372B7020070281400000A02030204281D000006281B0000062A133004008D00000002000011020302281500000A6F1600000A28140000060A060433012A020306281C000006020302281500000A6F1600000A042815000006021201FE15070000021201037D0F000004120102281500000A6F1600000A7D1000000412010418FE017D1100000407280400002B0418331102030203281000000617D728110000062A02030203281200000617D728130000062AD60445030000002200000012000000010000002A02030203281000000617DB28110000062A02030203281200000617DB28130000062A1E032D02172A182A0013300300FA00000003000011022829000006020328160000060A020328100000060B020328120000060C020708FE0372DB020070281400000A0207022803000006FE0516FE017221030070281400000A02067B1600000416FE017271030070281400000A020203280E00000602281800000A6F1900000AFE0572B5030070281400000A02067B1400000402281B00000AFE0316FE017203040070281400000A1200177D16000004020306281700000602067B13000004067B14000004281C00000A0D02096F1D00000A722F040070281400000A021204FE15050000021204037D0B0000041204067B140000047D0C0000041204067B130000047D0A0000041104280500002B2A00001330030029000000040000110228280000060203280C0000062D012A020316280D0000060228040000060A020617DB28050000062A000000133003004B0000000500001102282800000602281E00000A036F0600002B0A160B2B2D0607A3070000010C0208280C0000062C18020816280D0000060228040000060D020917DB28050000060717580B07068E6932CD2A001330030029000000040000110228280000060203280C0000062C012A020317280D0000060228040000060A020617D728050000062A000000133003004B0000000500001102282800000602281E00000A036F0600002B0A160B2B2D0607A3070000010C0208280C0000062D18020817280D0000060228040000060D020917D728050000060717580B07068E6932CD2A00133003003A00000006000011022828000006021200FE1506000002120002281500000A6F1600000A7D0D000004120002281500000A6F2000000A7D0E00000406280700002B2A1E0228230000062A3A022828000006020328020000062A8A0228280000060203022808000006FE057251040070281400000A020328070000062A8A0228280000060203022806000006FE0372C5040070281400000A020328090000062A8A0202280100000602281500000A6F1600000A282100000A723B050070281400000A2A6E0202281500000A6F2000000A166AFE01726F050070281400000A2A000042534A4201000100000000000C00000076342E302E33303331390000000005006C00000028070000237E0000940700003006000023537472696E677300000000C40D0000A8050000235553006C1300001000000023475549440000007C130000DC01000023426C6F620000000000000002000001571DA201090A000000FA01330016000001000000130000000800000016000000290000002E00000021000000040000000B0000000600000001000000060000000B000000010000000200000006000000070000000000AB0201000000000006007C015C040600BB015C040600680145040F007C0400000A00AB0135050A005F0535050A00250535050A00130135050A00900535050600D402CD020600FF00CD020A009C0135050A00270135050600F101CD0206006D05CD0206000E00CD020A00B00035050A00470235050A00240435050000000029000000000001000100010010005305000019000100010002010000E3040000290002002A000A011000F80100002D0006002A000A011000280200002D000A002A000A011000090200002D000D002A000A011000170200002D000F002A000A011000990200002D0012002A005680A20316010606320016015680FA0019015680CD0319015680580419010600AA051D0106006C0016010600EF0521010600B50324010600AA051D0106006C0016010600EF0521010600F1031D010600EF05210106006C00160106001E041D0106005C012701060018041D010600A0051D010600E60521010600B5032401060093002701502000000000860801047500010062200000000081080B042A0101007520000000008608D902300102008120000000008608BC05300102009320000000008108D10534010200A620000000008608E70230010300B820000000008108FD0234010300CB200000000086083D0330010400DD20000000008108530334010400F02000000000860851003001050002210000000081086400340105001521000000008600850039010600322100000000810082003F0107005021000000008600D600460109006D21000000008100E8004B010A008B21000000008600D10451010C00A821000000008100DD0456010D00C621000000008600BB0451010F00E321000000008100C60456011000012200000000860045015C01120024220000000081004D016301140048220000000086008A026C011700652200000000810096027201180084220000000086183F0479011A00E4220000000086005E0280011C00EC230000000086005C018901200048240000000081006D028F012200E12400000000810061018F01240017250000000081003E019601260020250000000086007A023401270028260000000086001C052A01280060260000000086009E049C012900B8260000000086000B052A012A00F0260000000086008B049C012B004827000000008600880506002C008E2700000000C600E90106002C009627000000008600D0032A012C00A527000000008600130334012D00C827000000008600690334012E00EB27000000008100120606002F000E28000000008100C50006002F0000000100E30100000100E30100000100E30100000100E30100000100E301000001002D05000001002D05000002009C0000000100770000000100770000000200580200000100770000000100770000000200E30100000100770000000100770000000200E301000001007700000002002D05000001007700000002002D0500000300630100000100040600000100040600000200A202000001003801000002002B0300000100B30500000200F60500000300930300000400C103000001007700000002006301000001007700000002006301000001007700000002005501000001006301000001007700000001002D0500000100B104000001002D0500000100B104000001001504000001002B0300000100810309003F04010011003F04060019003F040A0029003F04060061003F0406003100090110006900F5041500690000051B0069000100220069000B00270071004C052D006900BD0233006900C5023800690015003E0069001F00430071004C05490069007405500069007E055C0031003F0464003100FD056A003100B90070008900ED03750071003C02850031004E0289009100E2038E003100380292003100A4008E003100F803B5004900E904BD0031003004D40099000A06D9008900D9018E0039002206F10009000400020109000C000701090010000C010900140011012E000B00AB012E001300B4012E001B00D301430023000C01C1002B000C01E1002B000C0141012B000C0161012B000C01A1012B000C01E1012B000C0101022B000C017A009E00AA00C600CA00E7000200010000001804A2010000DD02A7010000D505A70100001903A70100006F03A70100006800A701020001000300010002000300020003000500020004000700010005000700020006000900010007000900020008000B00010009000B0002000A000D0001000B000D000480000000000000000000000000000000005F050000040000000000000000000000F9003A000000000002000000000000000000000000003505000000000300020004000200050002000600020007000200080002002300570025005700350099003500A5003500C1003F00E2003500EC0000000047657455496E7433320053657455496E7433320047657455496E7436340053657455496E743634003C4D6F64756C653E0076616C75655F5F0053797374656D2E507269766174652E436F72654C6962006765745F4C61737450726F706F73616C4964007365745F4C61737450726F706F73616C49640070726F706F73616C496400536574497357686974656C697374656400457865637574656400616C6C6F776564006765745F42616C616E636500494D657373616765006765745F4D65737361676500456E737572654E6F7450617961626C6500476574566F74696E67446561646C696E6500536574566F74696E67446561646C696E65004E6F6E650056616C756554797065006765745F53746174650049536D617274436F6E74726163745374617465004950657273697374656E74537461746500737461746500546F566F746500476574566F746500536574566F74650063757272656E74566F746500556E766F74650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E646578417474726962757465004465706C6F794174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465006765745F56616C75650076616C7565005265636569766500537472696E670050726F706F73616C41646465644C6F670046756E645261697365644C6F670050726F706F73616C566F7465644C6F670050726F706F73616C45786563757465644C6F67006765745F4C656E6774680049426C6F636B006765745F426C6F636B00626C6F636B0043726561746550726F706F73616C00566F746550726F706F73616C004578656375746550726F706F73616C0047657450726F706F73616C0053657450726F706F73616C0070726F706F73616C00536D617274436F6E74726163742E646C6C00476574426F6F6C00536574426F6F6C0053797374656D00456E756D006765745F4D696E51756F72756D006765745F4D696E566F74696E674475726174696F6E007365745F4D696E566F74696E674475726174696F6E005570646174654D696E566F74696E674475726174696F6E006D696E566F74696E674475726174696F6E006765745F4D6178566F74696E674475726174696F6E007365745F4D6178566F74696E674475726174696F6E005570646174654D6178566F74696E674475726174696F6E006D6178566F74696E674475726174696F6E00766F74696E674475726174696F6E0044656661756C744D61784475726174696F6E004465736372697074696F6E006465736372697074696F6E004E6F005472616E736665724F776E657273686970006765745F4E756D626572006765745F53656E646572005472616E73666572006765745F4F776E6572007365745F4F776E6572006E65774F776E657200566F746572004953657269616C697A6572006765745F53657269616C697A6572002E63746F720053797374656D2E446961676E6F7374696373005965730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F6465730057686974656C69737441646472657373657300426C61636B6C69737441646472657373657300616464726573736573004765744E6F566F746573005365744E6F566F74657300476574596573566F74657300536574596573566F746573006765745F53756363657373004765744164647265737300536574416464726573730057686974656C6973744164647265737300426C61636B6C69737441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300466F726D61740044414F436F6E747261637400536D617274436F6E7472616374004F626A6563740047657453747275637400536574537472756374004465706F73697400495472616E73666572526573756C7400526563697069656E74005265636970656E74007265636970656E74006765745F57686974656C6973746564436F756E74007365745F57686974656C6973746564436F756E7400526571756573746564416D6F756E7400616D6F756E740041737365727400696E64657800546F417272617900456E737572654F776E65724F6E6C79006F705F457175616C697479000000000B4F0077006E00650072000021570068006900740065006C006900730074006500640043006F0075006E00740000234D0069006E0056006F00740069006E0067004400750072006100740069006F006E0000234D006100780056006F00740069006E0067004400750072006100740069006F006E00001D4C00610073007400500072006F0070006F00730061006C0049006400001F570068006900740065006C00690073007400650064003A007B0030007D00001944006500610064006C0069006E0065003A007B0030007D00001959006500730056006F007400650073003A007B0030007D0000174E006F0056006F007400650073003A007B0030007D00001956006F00740065003A007B0030007D003A007B0031007D00001B500072006F0070006F00730061006C0073003A007B0030007D00007B4D0069006E0056006F00740069006E0067004400750072006100740069006F006E002000730068006F0075006C00640020006200650020006C006F0077006500720020007400680061006E0020006D006100780056006F00740069006E0067004400750072006100740069006F006E0028007B0030007D002900005D56006F00740069006E00670020006400750072006100740069006F006E002000730068006F0075006C00640020006200650020006200650074007700650065006E0020007B0030007D00200061006E00640020007B0031007D002E00006754006800650020006400650073006300720069007000740069006F006E0020006C0065006E006700740068002000630061006E00200062006500200075007000200074006F002000320030003000200063006800610072006100630074006500720073002E00003D5400680065002000630061006C006C006500720020006900730020006E006F0074002000770068006900740065006C00690073007400650064002E00002356006F00740069006E006700200069007300200063006C006F007300650064002E0000455400680065002000700072006F0070006F00730061006C00200076006F00740069006E00670020006900730020006E006F00740020007000610073007300650064002E00004F4D0069006E002000710075006F00720075006D00200066006F0072002000700072006F0070006F00730061006C0020006900730020006E006F007400200072006500610063006800650064002E0000435400680065002000700072006F0070006F00730061006C00200069007300200061006C00720065006100640079002000650078006500630075007400650064002E00004D56006F00740069006E00670020006900730020007300740069006C006C0020006F00700065006E00200066006F00720020007400680065002000700072006F0070006F00730061006C002E00002B49006E00730075006600660069006300690065006E0074002000620061006C0061006E00630065002E0000215400720061006E00730066006500720020006600610069006C00650064002E0000734D0069006E0056006F00740069006E0067004400750072006100740069006F006E002000730068006F0075006C00640020006200650020006C006F0077006500720020007400680061006E0020004D006100780056006F00740069006E0067004400750072006100740069006F006E002E0000754D006100780056006F00740069006E0067004400750072006100740069006F006E002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020004D0069006E0056006F00740069006E0067004400750072006100740069006F006E002E00003354006800650020006D006500740068006F00640020006900730020006F0077006E006500720020006F006E006C0079002E00003554006800650020006D006500740068006F00640020006900730020006E006F0074002000700061007900610062006C0065002E00000000002BD4CF21FA4D8240A3F96A8E7768B173000420010108032000010520010111110420001235052001111D0E062002010E111D042001090E052002010E090500020E0E1C042001020E052002010E020420010B0E052002010E0B0600030E0E1C1C063001011E000E040A01112007300102010E1E0005200101122105200201020E0420001245042000111D0A070508112009112011100320000804200012490320000B06300101011E00040A011110060702110C111C040A01111C0A070511200909122511140720021225111D0B03200002040A011114030701090907041D111D08111D09042000124D083001011D1E001D05040A01111D0407011118040A01111807000202111D111D087CEC85D7BEA7798E04A89300000400000000040100000004020000000206090306110C0306111D02060B02060E02060205200101111D03200009042001010905200102111D06200201111D020420010B0905200201090B04200109090520020109090620020909111D0820030109111D110C052001112009062002010911200620020112210908200409111D0B090E0520020109020620020109110C052001110C02052001011D05042800111D032800090801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730108010002000000000000000000000000000000000010000000000000000000000000000000C83D00000000000000000000E23D0000002000000000000000000000000000000000000000000000D43D0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000003000000C000000F43D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }; + + // https://github.com/stratisproject/CirrusSmartContracts/tree/master/Mainnet/PrivateYesNoVote + public static WhitelistedContract YesNoVoteContract = new WhitelistedContract() { Hash = "6bdaf42f95c0bd8ae7ffdd942c034ceaaa1be0774218536e830110e234e67d83", ByteCode = "4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010200B2B274940000000000000000E00022200B013000000E00000002000000000000AA2D0000002000000040000000000010002000000002000004000000000000000400000000000000006000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000582D00004F000000000000000000000000000000000000000000000000000000004000000C0000003C2D00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000B00D000000200000000E000000020000000000000000000000000000200000602E72656C6F6300000C0000000040000000020000001000000000000000000000000000004000004200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008C2D0000000000004800000002000500D8220000640A00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CE0203280500000A0202280600000A6F0700000A04D728050000060202280800000A6F0900000A2803000006020528100000062A4602280A00000A72010000706F0B00000A2A4A02280A00000A7201000070036F0C00000A2A4602280A00000A720D0000706F0D00000A2A4A02280A00000A720D000070036F0E00000A2A4602280A00000A72330000706F0F00000A2A4A02280A00000A7233000070036F1000000A2A4602280A00000A72450000706F0F00000A2A4A02280A00000A7245000070036F1000000A2A7602280A00000A7255000070038C07000001281100000A176F1200000A2A7202280A00000A7255000070038C07000001281100000A6F1300000A2A7202280A00000A7269000070038C07000001281100000A6F1400000A2A7602280A00000A7269000070038C07000001281100000A046F1500000A2A3A0228120000060203280A0000062A3A022812000006020328100000062A133002002B0000000100001102281600000A036F0100002B0A160B2B130607A3070000010C0208280A0000060717580B07068E6932E72A0013300300C300000002000011020202280800000A6F0900000A280B000006727B000070281800000A020202280800000A6F0900000A280C00000616FE0172BF000070281800000A0202280600000A6F0700000A022804000006FE0316FE0172F3000070281800000A032C151F790A0228060000060B020717D728070000062B131F6E0A0228080000060B020717D728090000060202280800000A6F0900000A06280D000006021202FE1503000002120202280800000A6F0900000A7D030000041202037D0400000408280200002B2A8A0202280800000A6F0900000A022802000006281A00000A7225010070281800000A2A000042534A4201000100000000000C00000076342E302E33303331390000000005006C000000BC030000237E0000280400006003000023537472696E67730000000088070000880100002355530010090000100000002347554944000000200900004401000023426C6F620000000000000002000001571DA201090A000000FA013300160000010000000F000000030000000400000012000000100000001A000000020000000400000002000000010000000400000008000000010000000200000001000000020000000000AC010100000000000600EE00640206001D0164020600DA004D020F00840200000A001503F7020A006800F7020A00E702F70206005E00CE010A000E01F7020A006D01F7020A004900F7020A008F00F70206006201CE0106002303CE010A002C02F702000000002900000000000100010001001000B40000001500010001000A0110002A03000021000300130051806002B9005180DE01B90006002602BC000600D000C00050200000000086184702C3000100842000000000860807022400040096200000000081081102CC000400A9200000000086087E011B000500BB200000000081089501D2000500CE20000000008608B502D7000600E020000000008108C202DB000600F3200000000086089D02D70007000521000000008108A902DB00070018210000000081003B01CC00080036210000000086001B02E00009005321000000008600C500E6000A007021000000008100CD00EC000B008E210000000086002302CC000D009D21000000008600CF02F3000E00AC210000000081004B01F3000F00E421000000008600D000F9001000B322000000008100430306001100000001007C0000000200D501000003009302000001005C01000001005C01000001005C01000001005C0100000100EF0200000100EF0200000100EF0200000100EF0200000200D50000000100EF0200000100930200000100930200000100D500090047020100110047020600190047020A004900470206002900470210002900740116005100F1011B00290052001F005900FC0124002900A00029006100D9022E006100E4023400610015003B0061001F00400061000100460061000B004B0069000E0351006100C60157006100BE015D006100E10162006100E901670029003802760079003B037B0029003403900029006901960039005303A20003000400B30003000800B6002E000B000B012E00130014012E001B003301610023003C016D0089000200010000001502FE000000990103010000C60207010000AD02070102000200030001000300030002000400050001000500050002000600070001000700070002000800090001000900090004800000000000000000000000000000000015030000040000000000000000000000AA003200000000000100020001000000000000000000F70200000000030002002F00840033009D00000000000047657455496E7433320053657455496E7433320047657455496E7436340053657455496E743634003C4D6F64756C653E0053797374656D2E507269766174652E436F72654C696200494D657373616765006765745F4D6573736167650056616C7565547970650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E745374617465006765745F50657273697374656E74537461746500507269766174655965734E6F566F746500476574566F746500536574566F746500766F74650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E6465784174747269627574650052756E74696D65436F6D7061746962696C69747941747472696275746500536574566F7465724578656375746500536574566F74657273457865637574650076616C756500537472696E67004C6F670049426C6F636B006765745F426C6F636B006765745F566F7465506572696F64456E64426C6F636B007365745F566F7465506572696F64456E64426C6F636B00536D617274436F6E74726163742E646C6C00476574426F6F6C00536574426F6F6C0053797374656D006475726174696F6E004E6F00476574436861720053657443686172006765745F4E756D626572006765745F53656E646572006765745F4F776E6572007365745F4F776E6572004973566F74657200536574566F746572004953657269616C697A6572006765745F53657269616C697A6572002E63746F720053797374656D2E446961676E6F7374696373005965730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F64657300616464726573736573006765745F4E6F566F746573007365745F4E6F566F746573006765745F596573566F746573007365745F596573566F74657300536574566F7465727300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300466F726D617400536D617274436F6E7472616374004F626A65637400566F74654576656E740041737365727400546F417272617900456E737572654F776E65724F6E6C79006F705F457175616C6974790000000B4F0077006E0065007200002556006F007400650050006500720069006F00640045006E00640042006C006F0063006B00001159006500730056006F00740065007300000F4E006F0056006F00740065007300001356006F007400650072003A007B0030007D00001156006F00740065003A007B0030007D000043530065006E0064006500720020006900730020006E006F007400200061007500740068006F00720069007A0065006400200074006F00200076006F00740065002E000033530065006E006400650072002000680061007300200061006C0072006500610064007900200076006F007400650064002E00003156006F00740069006E006700200070006500720069006F0064002000680061007300200065006E006400650064002E00005F4D007500730074002000620065002000740068006500200063006F006E007400720061006300740020006F0077006E0065007200200074006F00200061007500740068006F00720069007A006500200076006F0074006500720073002E000000000048B6B87C53B3FE409825037B88B058E30004200101080320000105200101111105200101121904200012290320000B042000122D042000111D0420001231052001111D0E062002010E111D0420010B0E052002010E0B042001090E052002010E090500020E0E1C052002010E02042001020E042001030E052002010E030807031D111D08111D042000123D083001011D1E001D05040A01111D0607030309110C05200201020E06300101011E00040A01110C07000202111D111D087CEC85D7BEA7798E027900026E000206030306111D0206020820030112190B1D0505200101111D042001010B03200009042001010905200102111D05200103111D06200201111D03052001011D050420010102042800111D0328000B032800090801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301080100020000000000040100000000000000000000000000000000000010000000000000000000000000000000802D000000000000000000009A2D00000020000000000000000000000000000000000000000000008C2D0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000AC3D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }; +} + +public class WhitelistedContract +{ + public string Hash; + + public string ByteCode; +} \ No newline at end of file diff --git a/Src/StratisUnity3d/Assets/Code/SmartContracts/WhitelistedContracts.cs.meta b/Src/StratisUnity3d/Assets/Code/SmartContracts/WhitelistedContracts.cs.meta new file mode 100644 index 0000000..f02f8b5 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Code/SmartContracts/WhitelistedContracts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4cd221de9eda2d47a42dc18a02e9e33 +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 index 16f768c..5280f93 100644 --- a/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs +++ b/Src/StratisUnity3d/Assets/Code/StratisUnityManager.cs @@ -2,6 +2,11 @@ using System.Text; using System.Threading.Tasks; using NBitcoin; +using Stratis.Bitcoin.Features.Wallet; +using Stratis.SmartContracts.CLR; +using Stratis.SmartContracts.CLR.Serialization; +using Stratis.SmartContracts.Core; +using Stratis.SmartContracts.RuntimeObserver; using Unity3dApi; using UnityEngine; using Network = NBitcoin.Network; @@ -20,6 +25,17 @@ public class StratisUnityManager private BitcoinPubKeyAddress address; + // Smart contract related + public ulong GasPrice { get; set; } = 100; + + public ulong GasLimit { get; set; } = 150000; + + public Money DefaultFee = "0.0001"; + + private MethodParameterStringSerializer methodParameterStringSerializer; + + private CallDataSerializer callDataSerializer; + public StratisUnityManager(Unity3dClient client, Network network, Mnemonic mnemonic) { this.client = client; @@ -29,6 +45,9 @@ public StratisUnityManager(Unity3dClient client, Network network, Mnemonic mnemo this.privateKey = this.mnemonic.DeriveExtKey().PrivateKey; this.publicKey = this.privateKey.PubKey; this.address = this.publicKey.GetAddress(network); + + this.methodParameterStringSerializer = new MethodParameterStringSerializer(this.network); + this.callDataSerializer = new CallDataSerializer(new ContractPrimitiveSerializer(this.network)); } public async Task GetBalanceAsync() @@ -56,7 +75,7 @@ public async Task SendTransactionAsync(string destinationAddress, Money .AddCoins(coins) .AddKeys(this.privateKey) .Send(addrTo, sendAmount) - .SendFees("0.0001") + .SendFees(DefaultFee) .SetChange(this.address) .BuildTransaction(true); @@ -89,7 +108,7 @@ public async Task SendOpReturnTransactionAsync(byte[] bytes) .AddCoins(coins) .AddKeys(this.privateKey) .Send(opReturnScript, Money.Zero) - .SendFees("0.0001") + .SendFees(DefaultFee) .SetChange(this.address) .BuildTransaction(true); @@ -112,4 +131,89 @@ private async Task GetCoinsAsync() return coins; } + + /// + /// Creates and sends smart contract create call. + /// + /// Compiled contract code represented as hex string. + /// An array of encoded strings containing the parameters (and their type) to pass to the smart contract + /// constructor when it is called. More information on the format of a parameter string is available + /// here. + public async Task SendCreateContractTransactionAsync(string contractCode, string[] parameters = null, Money amount = null) + { + Coin[] coins = await this.GetCoinsAsync().ConfigureAwait(false); + + ContractTxData txData; + if (parameters != null && parameters.Any()) + { + object[] methodParameters = methodParameterStringSerializer.Deserialize(parameters); + txData = new ContractTxData(1, (Gas)GasPrice, (Gas)GasLimit, contractCode.HexToByteArray(), methodParameters); + } + else + { + txData = new ContractTxData(1, (Gas)GasPrice, (Gas)GasLimit, contractCode.HexToByteArray()); + } + + ulong totalFee = (Gas)GasPrice * (Gas)GasLimit + DefaultFee; + + byte[] serializedTxData = this.callDataSerializer.Serialize(txData); + + ContractTxData deserialized = this.callDataSerializer.Deserialize(serializedTxData); + + var txBuilder = new TransactionBuilderSC(this.network); + Transaction tx = txBuilder + .AddCoins(coins) + .AddKeys(this.privateKey) + .Send(new Script(serializedTxData), amount ?? Money.Zero, true) + .SendFees(totalFee) + .SetChange(this.address) + .BuildTransaction(true); + + await client.SendTransactionAsync(new SendTransactionRequest() { Hex = tx.ToHex() }).ConfigureAwait(false); + + Debug.Log("Transaction sent."); + return tx.GetHash().ToString(); + } + + /// + /// Creates and sends smart contract call. + /// + /// Contract address + /// Method name to call + /// Call parameters + /// Money value to attach to call + /// + public async Task SendCallContractTransactionAsync(string contractAddr, string methodName, string[] parameters = null, Money amount = null) + { + Coin[] coins = await this.GetCoinsAsync().ConfigureAwait(false); + + uint160 addressNumeric = contractAddr.ToUint160(this.network); + + ContractTxData txData = null; + if (parameters != null && parameters.Any()) + { + object[] methodParameters = this.methodParameterStringSerializer.Deserialize(parameters); + txData = new ContractTxData(1, (Gas)GasPrice, (Gas)GasLimit, addressNumeric, methodName, methodParameters); + } + + ulong totalFee = (Gas)GasPrice * (Gas)GasLimit + DefaultFee; + + byte[] serializedTxData = this.callDataSerializer.Serialize(txData); + + ContractTxData deserialized = this.callDataSerializer.Deserialize(serializedTxData); + + var txBuilder = new TransactionBuilderSC(this.network); + Transaction tx = txBuilder + .AddCoins(coins) + .AddKeys(this.privateKey) + .Send(new Script(serializedTxData), amount ?? Money.Zero, true) + .SendFees(totalFee) + .SetChange(this.address) + .BuildTransaction(true); + + await client.SendTransactionAsync(new SendTransactionRequest() { Hex = tx.ToHex() }).ConfigureAwait(false); + + Debug.Log("SC call transaction sent. Id: " + tx.GetHash().ToString()); + return tx.GetHash().ToString(); + } } diff --git a/Src/StratisUnity3d/Assets/Code/Unity3dApi.cs b/Src/StratisUnity3d/Assets/Code/Unity3dApi.cs index 9ee28b7..7511b66 100644 --- a/Src/StratisUnity3d/Assets/Code/Unity3dApi.cs +++ b/Src/StratisUnity3d/Assets/Code/Unity3dApi.cs @@ -731,7 +731,190 @@ public async System.Threading.Tasks.Task TipAsync(System.Threading.Can client_.Dispose(); } } - + + + /// Success + /// A server side error occurred. + public System.Threading.Tasks.Task ReceiptAsync(string txHash) + { + return ReceiptAsync(txHash, 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 ReceiptAsync(string txHash, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/api/Unity3d/receipt?"); + if (txHash != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("txHash") + "=").Append(System.Uri.EscapeDataString(ConvertToString(txHash, 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 LocalCallAsync(LocalCallContractRequest body) + { + return LocalCallAsync(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 LocalCallAsync(LocalCallContractRequest body, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Unity3d/api/Unity3d/local-call"); + + 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"); + 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 + 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(); + } + } + protected struct ObjectResponseResult { public ObjectResponseResult(T responseObject, string responseText) @@ -835,219 +1018,414 @@ private string ConvertToString(object value, System.Globalization.CultureInfo cu } } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class BlockHeaderModel + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class BlockHeaderModel { [Newtonsoft.Json.JsonProperty("version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Version { get; set; } - + [Newtonsoft.Json.JsonProperty("merkleroot", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Merkleroot { get; set; } - + [Newtonsoft.Json.JsonProperty("nonce", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Nonce { get; set; } - + [Newtonsoft.Json.JsonProperty("bits", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Bits { get; set; } - + [Newtonsoft.Json.JsonProperty("previousblockhash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Previousblockhash { get; set; } - + [Newtonsoft.Json.JsonProperty("time", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Time { get; set; } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class BlockModel + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class BlockModel { [Newtonsoft.Json.JsonProperty("hash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Hash { get; set; } - + [Newtonsoft.Json.JsonProperty("confirmations", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Confirmations { get; set; } - + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Size { get; set; } - + [Newtonsoft.Json.JsonProperty("weight", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public long Weight { get; set; } - + [Newtonsoft.Json.JsonProperty("height", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Height { get; set; } - + [Newtonsoft.Json.JsonProperty("version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Version { get; set; } - + [Newtonsoft.Json.JsonProperty("versionHex", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string VersionHex { get; set; } - + [Newtonsoft.Json.JsonProperty("merkleroot", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Merkleroot { get; set; } - + [Newtonsoft.Json.JsonProperty("tx", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public System.Collections.Generic.ICollection Tx { get; set; } - + [Newtonsoft.Json.JsonProperty("time", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public long Time { get; set; } - + [Newtonsoft.Json.JsonProperty("mediantime", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public long Mediantime { get; set; } - + [Newtonsoft.Json.JsonProperty("nonce", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int Nonce { get; set; } - + [Newtonsoft.Json.JsonProperty("bits", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Bits { get; set; } - + [Newtonsoft.Json.JsonProperty("difficulty", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public double Difficulty { get; set; } - + [Newtonsoft.Json.JsonProperty("chainwork", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Chainwork { get; set; } - + [Newtonsoft.Json.JsonProperty("nTx", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int NTx { get; set; } - + [Newtonsoft.Json.JsonProperty("previousblockhash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Previousblockhash { get; set; } - + [Newtonsoft.Json.JsonProperty("nextblockhash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Nextblockhash { get; set; } - + [Newtonsoft.Json.JsonProperty("signature", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Signature { get; set; } - + [Newtonsoft.Json.JsonProperty("modifierv2", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Modifierv2 { get; set; } - + [Newtonsoft.Json.JsonProperty("flags", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Flags { get; set; } - + [Newtonsoft.Json.JsonProperty("hashproof", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Hashproof { get; set; } - + [Newtonsoft.Json.JsonProperty("blocktrust", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Blocktrust { get; set; } - + [Newtonsoft.Json.JsonProperty("chaintrust", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Chaintrust { get; set; } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class GetUTXOsResponseModel + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class ContractErrorMessage + { + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Value { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class GasModel + { + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Value { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class GetUTXOsResponseModel { [Newtonsoft.Json.JsonProperty("balanceSat", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public long BalanceSat { get; set; } - + [Newtonsoft.Json.JsonProperty("utxOs", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public System.Collections.Generic.ICollection Utxos { get; set; } - + [Newtonsoft.Json.JsonProperty("reason", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Reason { get; set; } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class ProblemDetails + + /// A class containing the necessary parameters to perform a local smart contract method call request. + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class LocalCallContractRequest + { + /// The height at which to query the contract's state. If unset, will default to the current chain tip. + [Newtonsoft.Json.JsonProperty("blockHeight", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long? BlockHeight { get; set; } + + /// The address of the smart contract containing the method. + [Newtonsoft.Json.JsonProperty("contractAddress", Required = Newtonsoft.Json.Required.Always)] + public string ContractAddress { get; set; } + + /// The name of the method to call. + [Newtonsoft.Json.JsonProperty("methodName", Required = Newtonsoft.Json.Required.Always)] + public string MethodName { get; set; } + + /// The amount of STRAT (or sidechain coin) to send to the smart contract address. + /// No funds are actually sent, but the Amount field allows + /// certain scenarios, where the funds sent dictates the result, to be checked. + [Newtonsoft.Json.JsonProperty("amount", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Amount { get; set; } + + /// The gas price to use. This is used to calculate the expected expenditure + /// if the method is run by a miner mining a call transaction rather than + /// locally. + [Newtonsoft.Json.JsonProperty("gasPrice", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long GasPrice { get; set; } + + /// The maximum amount of gas that can be spent executing this transaction. + /// Although the gas expenditure is theoretical rather than actual, + /// this limit cannot be exceeded even when the method is run locally. + [Newtonsoft.Json.JsonProperty("gasLimit", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long GasLimit { get; set; } + + /// A wallet address containing the funds to cover transaction fees, gas, and any funds specified in the + /// Amount field. + /// Note that because the method call is local no funds are spent. However, the concept of the sender address + /// is still valid and may need to be checked. + /// For example, some methods, such as a withdrawal method on an escrow smart contract, should only be executed + /// by the deployer, and in this case, it is the Sender address that identifies the deployer. + [Newtonsoft.Json.JsonProperty("sender", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Sender { get; set; } + + /// An array of encoded strings containing the parameters (and their type) to pass to the smart contract + /// method when it is called. More information on the + /// format of a parameter string is available + /// <a target="_blank" href="https://academy.stratisplatform.com/SmartContracts/working-with-contracts.html#parameter-serialization">here</a>. + [Newtonsoft.Json.JsonProperty("parameters", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Parameters { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class LocalExecutionResult + { + [Newtonsoft.Json.JsonProperty("internalTransfers", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection InternalTransfers { get; set; } + + [Newtonsoft.Json.JsonProperty("gasConsumed", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public GasModel GasConsumed { get; set; } + + [Newtonsoft.Json.JsonProperty("revert", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Revert { get; set; } + + [Newtonsoft.Json.JsonProperty("errorMessage", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public ContractErrorMessage ErrorMessage { get; set; } + + [Newtonsoft.Json.JsonProperty("return", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Return { get; set; } + + [Newtonsoft.Json.JsonProperty("logs", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Logs { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Log + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Uint160 Address { get; set; } + + [Newtonsoft.Json.JsonProperty("topics", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Topics { get; set; } + + [Newtonsoft.Json.JsonProperty("data", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public byte[] Data { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class LogResponse + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Address { get; set; } + + [Newtonsoft.Json.JsonProperty("topics", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Topics { get; set; } + + [Newtonsoft.Json.JsonProperty("data", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Data { get; set; } + + [Newtonsoft.Json.JsonProperty("log", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Log { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class ProblemDetails { [Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Type { get; set; } - + [Newtonsoft.Json.JsonProperty("title", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Title { get; set; } - + [Newtonsoft.Json.JsonProperty("status", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int? Status { get; set; } - + [Newtonsoft.Json.JsonProperty("detail", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Detail { get; set; } - + [Newtonsoft.Json.JsonProperty("instance", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Instance { get; set; } - + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); - + [Newtonsoft.Json.JsonExtensionData] public System.Collections.Generic.IDictionary AdditionalProperties { get { return _additionalProperties; } set { _additionalProperties = value; } } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class RawTxModel + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class RawTxModel { [Newtonsoft.Json.JsonProperty("hex", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Hex { get; set; } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class SendTransactionRequest + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class ReceiptResponse + { + [Newtonsoft.Json.JsonProperty("transactionHash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string TransactionHash { get; set; } + + [Newtonsoft.Json.JsonProperty("blockHash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string BlockHash { get; set; } + + [Newtonsoft.Json.JsonProperty("postState", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string PostState { get; set; } + + [Newtonsoft.Json.JsonProperty("gasUsed", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long GasUsed { get; set; } + + [Newtonsoft.Json.JsonProperty("from", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string From { get; set; } + + [Newtonsoft.Json.JsonProperty("to", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string To { get; set; } + + [Newtonsoft.Json.JsonProperty("newContractAddress", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string NewContractAddress { get; set; } + + [Newtonsoft.Json.JsonProperty("success", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Success { get; set; } + + [Newtonsoft.Json.JsonProperty("returnValue", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ReturnValue { get; set; } + + [Newtonsoft.Json.JsonProperty("bloom", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Bloom { get; set; } + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Error { get; set; } + + [Newtonsoft.Json.JsonProperty("logs", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Logs { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class SendTransactionRequest { [Newtonsoft.Json.JsonProperty("hex", Required = Newtonsoft.Json.Required.Always)] public string Hex { get; set; } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class TipModel + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class TipModel { [Newtonsoft.Json.JsonProperty("tipHash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string TipHash { get; set; } - + [Newtonsoft.Json.JsonProperty("tipHeight", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int TipHeight { get; set; } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class UTXOModel + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class TransferInfo + { + [Newtonsoft.Json.JsonProperty("from", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Uint160 From { get; set; } + + [Newtonsoft.Json.JsonProperty("to", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Uint160 To { get; set; } + + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Value { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Uint160 + { + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class UTXOModel { [Newtonsoft.Json.JsonProperty("hash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Hash { get; set; } - + [Newtonsoft.Json.JsonProperty("n", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int N { get; set; } - + [Newtonsoft.Json.JsonProperty("satoshis", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public long Satoshis { get; set; } - - + + } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")] - public partial class ValidatedAddress + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class ValidatedAddress { [Newtonsoft.Json.JsonProperty("isvalid", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public bool Isvalid { get; set; } - + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Address { get; set; } - + [Newtonsoft.Json.JsonProperty("scriptPubKey", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string ScriptPubKey { get; set; } - + [Newtonsoft.Json.JsonProperty("isscript", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public bool Isscript { get; set; } - + [Newtonsoft.Json.JsonProperty("iswitness", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public bool Iswitness { get; set; } - - + + } + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.1.0 (NJsonSchema v10.4.3.0 (Newtonsoft.Json v12.0.0.0))")] public partial class ApiException : System.Exception { diff --git a/Src/StratisUnity3d/Assets/Scenes/SmartContracts Example.unity b/Src/StratisUnity3d/Assets/Scenes/SmartContracts Example.unity new file mode 100644 index 0000000..5cbb333 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Scenes/SmartContracts Example.unity @@ -0,0 +1,344 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &676225199 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 676225202} + - component: {fileID: 676225201} + - component: {fileID: 676225200} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &676225200 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 676225199} + m_Enabled: 1 +--- !u!20 &676225201 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 676225199} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &676225202 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 676225199} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &995640566 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 995640568} + - component: {fileID: 995640567} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &995640567 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 995640566} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &995640568 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 995640566} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1308022401 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1308022403} + - component: {fileID: 1308022402} + m_Layer: 0 + m_Name: SCRIPTS + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1308022402 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1308022401} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8a723d1e9402f3745b758709a3d521b2, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1308022403 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1308022401} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 545.778, y: 245.38902, z: -0.002632141} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Src/StratisUnity3d/Assets/Scenes/SmartContracts Example.unity.meta b/Src/StratisUnity3d/Assets/Scenes/SmartContracts Example.unity.meta new file mode 100644 index 0000000..ea639f4 --- /dev/null +++ b/Src/StratisUnity3d/Assets/Scenes/SmartContracts Example.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f12c0330a3df9b44693158812b79bb06 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Src/StratisUnity3d/UserSettings/EditorUserSettings.asset b/Src/StratisUnity3d/UserSettings/EditorUserSettings.asset index b543455..d4a3839 100644 --- a/Src/StratisUnity3d/UserSettings/EditorUserSettings.asset +++ b/Src/StratisUnity3d/UserSettings/EditorUserSettings.asset @@ -8,6 +8,12 @@ EditorUserSettings: RecentlyUsedScenePath-0: value: 22424703114646680e0b0227036c6c111b07142f1f2b233e2867083debf42d flags: 0 + RecentlyUsedScenePath-1: + value: 22424703114646680e0b0227036c7a08171a0826291b2535232c5326ece92021 + flags: 0 + RecentlyUsedScenePath-2: + value: 22424703114646680e0b0227036c6c1d17050c09232632222c2a0920a2c52c39eff73aeca92f31352d1b + flags: 0 vcSharedLogLevel: value: 0d5e400f0650 flags: 0