diff --git a/Mainnet/HashBattle/HashBattle.sln b/Mainnet/HashBattle/HashBattle.sln new file mode 100644 index 00000000..11cccce2 --- /dev/null +++ b/Mainnet/HashBattle/HashBattle.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31624.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBattle", "HashBattle\HashBattle.csproj", "{D711FA52-750E-481B-9BC5-2E07EBF58240}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBattleTest", "HashBattleTest\HashBattleTest.csproj", "{2A7F2670-A17F-46E6-BF35-A2C55BC7DCB1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D711FA52-750E-481B-9BC5-2E07EBF58240}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D711FA52-750E-481B-9BC5-2E07EBF58240}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D711FA52-750E-481B-9BC5-2E07EBF58240}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D711FA52-750E-481B-9BC5-2E07EBF58240}.Release|Any CPU.Build.0 = Release|Any CPU + {2A7F2670-A17F-46E6-BF35-A2C55BC7DCB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A7F2670-A17F-46E6-BF35-A2C55BC7DCB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A7F2670-A17F-46E6-BF35-A2C55BC7DCB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A7F2670-A17F-46E6-BF35-A2C55BC7DCB1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {88A96B52-6F00-40C9-AA28-C3D79E3BC0DF} + EndGlobalSection +EndGlobal diff --git a/Mainnet/HashBattle/HashBattle/Arena.cs b/Mainnet/HashBattle/HashBattle/Arena.cs new file mode 100644 index 00000000..8b39512f --- /dev/null +++ b/Mainnet/HashBattle/HashBattle/Arena.cs @@ -0,0 +1,299 @@ +using Stratis.SmartContracts; +using System; +using System.Text; + +/// +/// A Stratis smart contract for running a game battle where owner will start the battle and maximum 4 users can enter a battle +/// +public class Arena : SmartContract +{ + private void SetBattle(ulong battleId, BattleMain battle) + { + State.SetStruct($"battle:{battleId}", battle); + } + public BattleMain GetBattle(ulong battleId) + { + return State.GetStruct($"battle:{battleId}"); + } + private void SetUser(ulong battleId, Address address, BattleUser user) + { + State.SetStruct($"user:{battleId}:{address}", user); + } + public BattleUser GetUser(ulong battleId, Address address) + { + return State.GetStruct($"user:{battleId}:{address}"); + } + private void SetHighestScorer(ulong battleId, BattleHighestScorer highestScorer) + { + State.SetStruct($"scorer:{battleId}", highestScorer); + } + public BattleHighestScorer GetHighestScorer(ulong battleId) + { + return State.GetStruct($"scorer:{battleId}"); + } + private void SetUserIndex(ulong battleId, uint userindex) + { + State.SetUInt32($"user:{battleId}", userindex); + } + private uint GetUserIndex(ulong battleId) + { + return State.GetUInt32($"user:{battleId}"); + } + private void SetScoreSubmittedCount(ulong battleId, uint scoresubmitcount) + { + State.SetUInt32($"scoresubmit:{battleId}", scoresubmitcount); + } + private uint GetScoreSubmittedCount(ulong battleId) + { + return State.GetUInt32($"scoresubmit:{battleId}"); + } + /// + /// Set the address deploying the contract as battle owner + /// + public Address Owner + { + get => State.GetAddress(nameof(Owner)); + private set => State.SetAddress(nameof(Owner), value); + } + public Address PendingOwner + { + get => State.GetAddress(nameof(PendingOwner)); + private set => State.SetAddress(nameof(PendingOwner), value); + } + public uint MaxUsers + { + get => State.GetUInt32(nameof(MaxUsers)); + private set => State.SetUInt32(nameof(MaxUsers), value); + } + /// + /// Set the unique battleId of each battle + /// + public ulong NextBattleId + { + get => State.GetUInt64(nameof(NextBattleId)); + private set => State.SetUInt64(nameof(NextBattleId), value); + } + + public Arena(ISmartContractState smartContractState, uint maxUsers) : base(smartContractState) + { + Owner = Message.Sender; + MaxUsers = maxUsers; + NextBattleId = 1; + } + + /// + /// Only owner can set new owner and new owner will be in pending state + /// till new owner will call method. + /// + /// The new owner which is going to be in pending state + public void SetPendingOwner(Address newOwner) + { + EnsureOwnerOnly(); + PendingOwner = newOwner; + + Log(new OwnershipTransferRequestedLog { CurrentOwner = Owner, PendingOwner = newOwner }); + } + + /// + /// Waiting be called after new owner is requested by call. + /// Pending owner will be new owner after successfull call. + /// + public void ClaimOwnership() + { + var newOwner = PendingOwner; + + Assert(newOwner == Message.Sender, "ClaimOwnership must be called by the new(pending) owner."); + + var oldOwner = Owner; + Owner = newOwner; + PendingOwner = Address.Zero; + + Log(new OwnershipTransferredLog { PreviousOwner = oldOwner, NewOwner = newOwner }); + } + /// + /// Battle owner will start the battle + /// + public ulong StartBattle(ulong fee) + { + Assert(Message.Sender == Owner, "Only battle owner can start game."); + Assert(fee < ulong.MaxValue / MaxUsers, "Fee is too high"); + + var battleId = NextBattleId; + NextBattleId = battleId + 1; + + var battle = new BattleMain + { + BattleId = battleId, + Fee = fee, + Users = new Address[MaxUsers] + }; + SetBattle(battleId, battle); + + Log(new BattleStartedLog { BattleId = battleId, Address = Message.Sender }); + return battleId; + } + /// + /// 4 different user will enter the battle + /// + public void EnterBattle(ulong battleId) + { + var battle = GetBattle(battleId); + + Assert(battle.Winner == Address.Zero, "Battle not found."); + + Assert(battle.Fee == Message.Value, "Battle fee is not matching with entry fee paid."); + + var user = GetUser(battleId, Message.Sender); + + Assert(!user.ScoreSubmitted, "The user already submitted score."); + + SetUser(battleId, Message.Sender, user); + + var userindex = GetUserIndex(battleId); + Assert(userindex != MaxUsers, "Max user reached for this battle."); + battle.Users.SetValue(Message.Sender, userindex); + SetUserIndex(battleId, userindex + 1); + + SetBattle(battleId, battle); + + Log(new BattleEnteredLog { BattleId = battleId, Address = Message.Sender }); + } + /// + /// 4 different user will end the battle and submit the score + /// + public void EndBattle(Address userAddress, ulong battleId, uint score) + { + Assert(Message.Sender == Owner, "Only battle owner can end game."); + + var ScoreSubmittedCount = GetScoreSubmittedCount(battleId); + Assert(ScoreSubmittedCount < MaxUsers, "All users already submitted score."); + + var battle = GetBattle(battleId); + + Assert(battle.Winner == Address.Zero, "Battle not found."); + + var user = GetUser(battleId, userAddress); + + Assert(!user.ScoreSubmitted, "The user already submitted score."); + + user.ScoreSubmitted = true; + + SetUser(battleId, userAddress, user); + + ScoreSubmittedCount += 1; + SetScoreSubmittedCount(battleId, ScoreSubmittedCount); + + var highestScorer = GetHighestScorer(battleId); + + if (score > highestScorer.Score) + { + highestScorer.Score = score; + highestScorer.HighestScorer = userAddress; + highestScorer.HighestScoreCount = 1; + + SetHighestScorer(battleId, highestScorer); + } + else if (score == highestScorer.Score) + { + highestScorer.HighestScoreCount++; + SetHighestScorer(battleId, highestScorer); + } + + if (ScoreSubmittedCount == MaxUsers) + { + highestScorer = GetHighestScorer(battleId); + if (highestScorer.HighestScoreCount > 1) + CancelBattle(battle); + else + ProcessWinner(battle, highestScorer.HighestScorer); + } + + Log(new BattleEndedLog { BattleId = battleId, Address = Message.Sender }); + } + /// + /// Get winner address + /// + public Address GetWinner(ulong battleId) + { + var battle = GetBattle(battleId); + return battle.Winner; + } + /// + /// Process winner when all user scores are submitted + /// + private void ProcessWinner(BattleMain battle, Address winnerAddress) + { + battle.Winner = winnerAddress; + SetBattle(battle.BattleId, battle); + ProcessPrize(battle); + } + /// + /// Send 3/4 amount to winner and 1/4 amount to battle owner + /// + private void ProcessPrize(BattleMain battle) + { + var prize = battle.Fee * (MaxUsers - 1); + Transfer(battle.Winner, prize); + Transfer(Owner, battle.Fee); + } + /// + /// Cancel battle and refund the fee amount + /// + private void CancelBattle(BattleMain battle) + { + battle.IsCancelled = true; + SetBattle(battle.BattleId, battle); + + Transfer(battle.Users[0], battle.Fee); + Transfer(battle.Users[1], battle.Fee); + Transfer(battle.Users[2], battle.Fee); + Transfer(battle.Users[3], battle.Fee); + } + private void EnsureOwnerOnly() + { + Assert(Message.Sender == Owner, "The method is owner only."); + } + public struct BattleMain + { + public ulong BattleId; + public Address Winner; + public Address[] Users; + public ulong Fee; + public bool IsCancelled; + } + public struct BattleUser + { + public bool ScoreSubmitted; + } + public struct BattleHighestScorer + { + public uint Score; + public uint HighestScoreCount; + public Address HighestScorer; + } + public struct OwnershipTransferredLog + { + [Index] public Address PreviousOwner; + [Index] public Address NewOwner; + } + public struct OwnershipTransferRequestedLog + { + [Index] public Address CurrentOwner; + [Index] public Address PendingOwner; + } + public struct BattleStartedLog + { + [Index] public ulong BattleId; + [Index] public Address Address; + } + public struct BattleEnteredLog + { + [Index] public ulong BattleId; + [Index] public Address Address; + } + public struct BattleEndedLog + { + [Index] public ulong BattleId; + [Index] public Address Address; + } +} \ No newline at end of file diff --git a/Mainnet/HashBattle/HashBattle/HashBattle.csproj b/Mainnet/HashBattle/HashBattle/HashBattle.csproj new file mode 100644 index 00000000..1f433bdc --- /dev/null +++ b/Mainnet/HashBattle/HashBattle/HashBattle.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp2.1 + + 8.0 + + + + + diff --git a/Mainnet/HashBattle/HashBattleTest/ArenaTest.cs b/Mainnet/HashBattle/HashBattleTest/ArenaTest.cs new file mode 100644 index 00000000..b2227cf1 --- /dev/null +++ b/Mainnet/HashBattle/HashBattleTest/ArenaTest.cs @@ -0,0 +1,140 @@ +using System; +using Moq; +using Stratis.SmartContracts; +using Stratis.SmartContracts.CLR; +using Xunit; +using static Arena; + +namespace HashBattleTest +{ + public class ArenaTest + { + private readonly IPersistentState state; + private readonly Mock mockContractState; + private readonly Mock mockPersistentState; + private readonly Mock mockContractLogger; + private readonly Mock mTransactionExecutor; + private Address contract; + private Address ownerAddress; + private Address playerAddress1; + private Address playerAddress2; + private Address playerAddress3; + private Address playerAddress4; + + public ArenaTest() + { + this.state = new InMemoryState(); + this.mockPersistentState = new Mock(); + this.mockContractState = new Mock(); + this.mockContractLogger = new Mock(); + this.mTransactionExecutor = new Mock(); + this.mockContractState.Setup(s => s.PersistentState).Returns(this.state); + this.mockContractState.Setup(s => s.ContractLogger).Returns(this.mockContractLogger.Object); + this.mockContractState.Setup(s => s.InternalTransactionExecutor).Returns(this.mTransactionExecutor.Object); + this.contract = "0x0000000000000000000000000000000000000001".HexToAddress(); + this.ownerAddress = "0x0000000000000000000000000000000000000002".HexToAddress(); + this.playerAddress1 = "0x0000000000000000000000000000000000000003".HexToAddress(); + this.playerAddress2 = "0x0000000000000000000000000000000000000004".HexToAddress(); + this.playerAddress3 = "0x0000000000000000000000000000000000000005".HexToAddress(); + this.playerAddress4 = "0x0000000000000000000000000000000000000006".HexToAddress(); + } + + + [Fact] + public void TestBattle() + { + Arena arena = StartBattleTest(); + Player1EnterGameTest(arena); + Player2EnterGameTest(arena); + Player3EnterGameTest(arena); + Player4EnterGameTest(arena); + Player1EndGameTest(arena); + Player2EndGameTest(arena); + Player3EndGameTest(arena); + Player4EndGameTest(arena); + GetGameWinnerTest(arena); + } + + private Arena StartBattleTest() + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); + Arena arena = new Arena(this.mockContractState.Object, 4); + ulong battleId = arena.StartBattle(1); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleStartedLog { BattleId = battleId, Address = this.ownerAddress })); + return arena; + } + + private void Player1EnterGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress1, 1)); + arena.EnterBattle(1); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress1 })); + } + + private void Player2EnterGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress2, 1)); + arena.EnterBattle(1); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress2 })); + } + + private void Player3EnterGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress3, 1)); + arena.EnterBattle(1); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress3 })); + } + + private void Player4EnterGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress4, 1)); + arena.EnterBattle(1); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress4 })); + } + + private void Player1EndGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); + arena.EndBattle(this.playerAddress1, 1, 10); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); + } + + private void Player2EndGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); + arena.EndBattle(this.playerAddress2, 1, 20); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); + } + + private void Player3EndGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); + arena.EndBattle(this.playerAddress3, 1, 30); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); + } + + private void Player4EndGameTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); + arena.EndBattle(this.playerAddress4, 1, 40); + + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); + } + + private void GetGameWinnerTest(Arena arena) + { + this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); + Address winner = arena.GetWinner(1); + + Assert.Equal(this.playerAddress4, winner); + } + } +} diff --git a/Mainnet/HashBattle/HashBattleTest/HashBattleTest.csproj b/Mainnet/HashBattle/HashBattleTest/HashBattleTest.csproj new file mode 100644 index 00000000..b71e8f64 --- /dev/null +++ b/Mainnet/HashBattle/HashBattleTest/HashBattleTest.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Mainnet/HashBattle/HashBattleTest/InMemoryState.cs b/Mainnet/HashBattle/HashBattleTest/InMemoryState.cs new file mode 100644 index 00000000..c561a923 --- /dev/null +++ b/Mainnet/HashBattle/HashBattleTest/InMemoryState.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using Stratis.SmartContracts; + +namespace HashBattleTest +{ + public class InMemoryState : IPersistentState + { + private readonly Dictionary storage = new Dictionary(); + + public bool IsContractResult { get; set; } + + public void Clear(string key) => this.storage.Remove(key); + + public T GetValue(string key) => (T)this.storage.GetValueOrDefault(key, default(T)); + + public void AddOrReplace(string key, object value) + { + if (!this.storage.TryAdd(key, value)) + { + this.storage[key] = value; + } + } + + public Address GetAddress(string key) => this.GetValue
(key); + + public T[] GetArray(string key) => this.GetValue(key); + + public bool GetBool(string key) => this.GetValue(key); + + public byte[] GetBytes(byte[] key) => throw new NotImplementedException(); + + public byte[] GetBytes(string key) => this.GetValue(key); + + public char GetChar(string key) => this.GetValue(key); + + public int GetInt32(string key) => this.GetValue(key); + + public long GetInt64(string key) => this.GetValue(key); + + public string GetString(string key) => this.GetValue(key); + + public T GetStruct(string key) + where T : struct => this.GetValue(key); + + public uint GetUInt32(string key) => this.GetValue(key); + + public ulong GetUInt64(string key) => this.GetValue(key); + + public UInt128 GetUInt128(string key) => this.GetValue(key); + + public UInt256 GetUInt256(string key) => this.GetValue(key); + + public bool IsContract(Address address) => this.IsContractResult; + + public void SetAddress(string key, Address value) => this.AddOrReplace(key, value); + + public void SetArray(string key, Array a) => this.AddOrReplace(key, a); + + public void SetBool(string key, bool value) => this.AddOrReplace(key, value); + + public void SetBytes(byte[] key, byte[] value) + { + throw new NotImplementedException(); + } + + public void SetBytes(string key, byte[] value) => this.AddOrReplace(key, value); + + public void SetChar(string key, char value) => this.AddOrReplace(key, value); + + public void SetInt32(string key, int value) => this.AddOrReplace(key, value); + + public void SetInt64(string key, long value) => this.AddOrReplace(key, value); + + public void SetString(string key, string value) => this.AddOrReplace(key, value); + + public void SetStruct(string key, T value) + where T : struct => this.AddOrReplace(key, value); + + public void SetUInt32(string key, uint value) => this.AddOrReplace(key, value); + + public void SetUInt64(string key, ulong value) => this.AddOrReplace(key, value); + + public void SetUInt128(string key, UInt128 value) => this.AddOrReplace(key, value); + + public void SetUInt256(string key, UInt256 value) => this.AddOrReplace(key, value); + } +} diff --git a/Mainnet/HashBattle/README.MD b/Mainnet/HashBattle/README.MD new file mode 100644 index 00000000..ea8daf98 --- /dev/null +++ b/Mainnet/HashBattle/README.MD @@ -0,0 +1,15 @@ +# Hashbattle Smart Contract + +**Compiler Version** +``` +v2.0.0 +``` +**Contract Hash** +``` +07e52e71a4ce2afa30d03280b15a205b53eb7131c5fae709031fdf570d4a80b2 +``` + +**Contract Byte Code** +``` +4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010200E6EB4ACA0000000000000000E00022200B013000001A000000020000000000001E380000002000000040000000000010002000000002000004000000000000000400000000000000006000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000CC3700004F000000000000000000000000000000000000000000000000000000004000000C000000B03700001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E746578740000002418000000200000001A000000020000000000000000000000000000200000602E72656C6F6300000C0000000040000000020000001C0000000000000000000000000000400000420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003800000000000048000000020005000C270000A410000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007602280500000A7201000070038C0B000001280600000A046F0100002B2A7202280500000A7201000070038C0B000001280600000A6F0200002B2A8E02280500000A7217000070038C0B000001048C06000001280900000A056F0300002B2A8A02280500000A7217000070038C0B000001048C06000001280900000A6F0400002B2A7602280500000A7231000070038C0B000001280600000A046F0500002B2A7202280500000A7231000070038C0B000001280600000A6F0600002B2A7602280500000A7247000070038C0B000001280600000A046F0A00000A2A7202280500000A7247000070038C0B000001280600000A6F0B00000A2A7602280500000A7259000070038C0B000001280600000A046F0A00000A2A7202280500000A7259000070038C0B000001280600000A6F0B00000A2A4602280500000A72790000706F0C00000A2A4A02280500000A7279000070036F0D00000A2A4602280500000A72850000706F0C00000A2A4A02280500000A7285000070036F0D00000A2A4602280500000A729F0000706F0B00000A2A4A02280500000A729F000070036F0A00000A2A4602280500000A72B10000706F0E00000A2A4A02280500000A72B1000070036F0F00000A2AA20203281000000A0202281100000A6F1200000A280C0000060204281000000602176A28120000062A13300300320000000100001102281D0000060203280E000006021200FE1507000002120002280B0000067D0C0000041200037D0D00000406280700002B2A0000133003005C0000000200001102280D0000060A020602281100000A6F1200000A281400000A72CB000070281500000A02280B0000060B0206280C000006027E1600000A280E000006021202FE15060000021202077D0A0000041202067D0B00000408280800002B2A13300400A9000000030000110202281100000A6F1200000A02280B000006281400000A723D010070281500000A0203156A02280F0000066E5CFE057281010070281500000A0228110000060A0206176AD728120000061202FE15030000021202067D010000041202037D04000004120202280F0000068D060000017D03000004080B0206072801000006021203FE15080000021203067D0E000004120302281100000A6F1200000A7D0F00000409280900002B062A00000013300400F300000004000011020328020000060A02067B020000047E1600000A281400000A72A1010070281500000A02067B0400000402281100000A6F1700000AFE0172C5010070281500000A020302281100000A6F1200000A28040000060B02077B0600000416FE017225020070281500000A020302281100000A6F1200000A072803000006020328080000060C020802280F000006FE0116FE017269020070281500000A067B0300000402281100000A6F1200000A8C06000001086E6F1800000A02030817D728070000060203062801000006021203FE15090000021203037D10000004120302281100000A6F1200000A7D1100000409280A00002B2A001330040045010000050000110202281100000A6F1200000A02280B000006281400000A72AD020070281500000A0204280A0000060A020602280F000006FE0572ED020070281500000A020428020000060B02077B020000047E1600000A281400000A72A1010070281500000A02040328040000060C02087B0600000416FE017225020070281500000A1202177D060000040204030828030000060617D70A0204062809000006020428060000060D05097B0700000436221203057D070000041203037D090000041203177D0800000402040928050000062B1D05097B07000004331412037C08000004254B17D75402040928050000060602280F0000063327020428060000060D097B080000041736090207281C0000062B0D0207097B09000004281A000006021204FE150A0000021204047D12000004120402281100000A6F1200000A7D130000041104280B00002B2A36020328020000067B020000042A760F01047D0200000402037B010000040328010000060203281B0000062A000000133003003300000006000011037B0400000402280F00000617DB6ED90A02037B0200000406281900000A260202280B000006037B04000004281900000A262A00133003007A000000000000000F01177D0500000402037B0100000403280100000602037B0300000416A306000001037B04000004281900000A2602037B0300000417A306000001037B04000004281900000A2602037B0300000418A306000001037B04000004281900000A2602037B0300000419A306000001037B04000004281900000A262A8A0202281100000A6F1200000A02280B000006281400000A7233030070281500000A2A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C000000EC050000237E0000580600001C05000023537472696E677300000000740B00006803000023555300DC0E0000100000002347554944000000EC0E0000B801000023426C6F6200000000000000020000015715A201090A000000FA01330016000001000000100000000A000000130000001D00000022000000190000000D000000060000000100000004000000080000000100000002000000080000000B00000000004902010000000000060068019D03060097019D03060054018A030F00BD0300000A00450427040A00170427040A001C0127040600FC005B020A00880127040A0043012704060022005B020600DB015B02060053045B020A009D0027040600FA045B020A006E042704000000002900000000000100010001001000320000001500010001000A01100062020000210001001E000A01100064030000210006001E000A01100020030000210007001E000A0110000202000021000A001E000A0110002B02000021000C001E000A0110001A02000021000E001E000A011000F1010000210010001E000A011000E2010000210012001E0006006800F2000600A6029B000600E903F50006009500F20006007A00FA0006008600FA0006000601FD000600AC04FD00060048039B000600F3029B0006000E039B00060001039B000600E6029B0006006800F200060017049B0006006800F200060017049B0006006800F200060017049B005020000000008100DF00000101006E20000000008600D500070103008B2000000000810077030D010400AF200000000086006F0316010700D22000000000810045031E010900F020000000008600340325010B000D21000000008100E3042B010C002B21000000008100D60431010E00482100000000810095042B010F0066210000000081007E04310111008321000000008608AD026E0012009521000000008108B70236011200A821000000008608C1026E001300BA21000000008108D20236011300CD21000000008608CC033C011400DF21000000008108D90340011400F2210000000086084F00BD0015000422000000008108600045011500172200000000861884034A0116004022000000008600E302360118008022000000008600720206001900E822000000008600E90051011900A023000000008600C90045011A00A024000000008600B20056011B00F125000000008600A3025E011E00FF25000000008100950264011F002026000000008100CE016C0121006026000000008100BC006C012200E62600000000810000050600230000000100710000000200F500000001007100000001007100000002001F04000003007F03000001007100000002001F0400000100710000000200560300000100710000000100710000000200F00400000100710000000100710000000200BE0400000100710000000100C80100000100C80100000100C80100000100C80100000100300100000200E60300000100170300000100990000000100710000000100FD03000002007100000003000C0100000100710000000100F50000000200EF0300000100F50000000100F500090084030100110084030600190084030A0049008403060029001201100061003E041500510064041B0051005A04280061003E042F0051000B004000510001004600510009044B0051001404510051001500580051001F005D002900840363002900A6006900710081026E00290045027800310010058D002900CF04950031006D029B007100B501BD007900BF01C10029008C02E1002E000B007F012E00130088012E001B00A70141012300B00161012300B00181012300B001A1012300B001C1012300B001E1012300B00101022300B00121022300B00141022300B00161022300B00173008400A400B300CC00DD000200010000001A0372010000E60272010000DD037701000064007B0102000B00030001000C00030002000D00050001000E00050002000F00070001001000070002001100090001001200090004800000000000000000000000000000000045040000040000000000000000000000E9003800000000000200000000000000000000000000270400000000030002000400020005000200060002000700020008000200090002000A0002000F002300110023000F003600110036000F003B0011003B0027007F0027009F002700AE002700C7002700D800000000000047657455496E7433320053657455496E7433320047657455496E7436340053657455496E743634003C4D6F64756C653E004172656E610053797374656D2E507269766174652E436F72654C6962006765745F4E657874426174746C654964007365745F4E657874426174746C65496400626174746C65496400497343616E63656C6C65640053636F72655375626D6974746564004665650066656500494D657373616765006765745F4D65737361676500456E64426174746C650043616E63656C426174746C6500456E746572426174746C6500476574426174746C6500536574426174746C65005374617274426174746C6500626174746C650056616C7565547970650053636F72650073636F7265006765745F53746174650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E7453746174650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E6465784174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465006765745F56616C75650053657456616C75650076616C75650050726F636573735072697A6500537472696E6700426174746C65456E6465644C6F6700426174746C65456E74657265644C6F67004F776E6572736869705472616E736665727265644C6F6700426174746C65537461727465644C6F67004F776E6572736869705472616E736665725265717565737465644C6F6700536D617274436F6E74726163742E646C6C0053797374656D00426174746C654D61696E005A65726F00436C61696D4F776E657273686970006765745F53656E646572005472616E736665720050726F6365737357696E6E65720047657457696E6E6572006765745F4F776E6572007365745F4F776E6572006765745F50656E64696E674F776E6572007365745F50656E64696E674F776E65720053657450656E64696E674F776E65720050726576696F75734F776E65720043757272656E744F776E6572004E65774F776E6572006E65774F776E657200426174746C654869676865737453636F726572004765744869676865737453636F726572005365744869676865737453636F726572006869676865737453636F72657200426174746C6555736572004765745573657200536574557365720075736572002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573006765745F4D61785573657273007365745F4D61785573657273006D617855736572730077696E6E65724164647265737300757365724164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300466F726D617400536D617274436F6E7472616374004F626A656374004765745374727563740053657453747275637400495472616E73666572526573756C740047657453636F72655375626D6974746564436F756E740053657453636F72655375626D6974746564436F756E74004869676865737453636F7265436F756E740073636F72657375626D6974636F756E74004173736572740047657455736572496E6465780053657455736572496E6465780075736572696E64657800417272617900456E737572654F776E65724F6E6C79006F705F457175616C69747900001562006100740074006C0065003A007B0030007D00001975007300650072003A007B0030007D003A007B0031007D000015730063006F007200650072003A007B0030007D00001175007300650072003A007B0030007D00001F730063006F00720065007300750062006D00690074003A007B0030007D00000B4F0077006E00650072000019500065006E00640069006E0067004F0077006E006500720000114D00610078005500730065007200730000194E0065007800740042006100740074006C00650049006400007143006C00610069006D004F0077006E0065007200730068006900700020006D007500730074002000620065002000630061006C006C0065006400200062007900200074006800650020006E00650077002800700065006E00640069006E006700290020006F0077006E00650072002E0000434F006E006C007900200062006100740074006C00650020006F0077006E00650072002000630061006E002000730074006100720074002000670061006D0065002E00001F460065006500200069007300200074006F006F0020006800690067006800002342006100740074006C00650020006E006F007400200066006F0075006E0064002E00005F42006100740074006C006500200066006500650020006900730020006E006F00740020006D00610074006300680069006E00670020007700690074006800200065006E007400720079002000660065006500200070006100690064002E00004354006800650020007500730065007200200061006C007200650061006400790020007300750062006D00690074007400650064002000730063006F00720065002E0000434D00610078002000750073006500720020007200650061006300680065006400200066006F00720020007400680069007300200062006100740074006C0065002E00003F4F006E006C007900200062006100740074006C00650020006F0077006E00650072002000630061006E00200065006E0064002000670061006D0065002E00004541006C006C00200075007300650072007300200061006C007200650061006400790020007300750062006D00690074007400650064002000730063006F00720065002E00003354006800650020006D006500740068006F00640020006900730020006F0077006E006500720020006F006E006C0079002E000000F641A50CB4EA5F4D9C24BB4EACC176CA0004200101080320000105200101111104200012290500020E0E1C07300102010E1E00040A01110C063001011E000E0600030E0E1C1C040A011110040A011114052002010E09042001090E05200111190E062002010E11190420010B0E052002010E0B05200101121D04200012390420001119040701111C06300101011E00040A01111C080703111911191118070002021119111905200201020E03061119040A0111180907040B110C110C1120040A011120090704110C11100911240320000B052002011C0A040A0111240B070509110C111011141128040A0111280307010B072002124111190B087CEC85D7BEA7798E02060B04061D1119020602020609062002010B110C052001110C0B082003010B1119111007200211100B1119062002010B111405200111140B052002010B09042001090B052001011119032000090420010109042001010B06200201121D090420010B0B0720030111190B0905200111190B07200201110C111905200101110C0428001119032800090328000B0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301080100020000000000040100000000000000000000000000000000000010000000000000000000000000000000F437000000000000000000000E38000000200000000000000000000000000000000000000000000000380000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000C000000203800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +``` diff --git a/Testnet/HashBattle/HashBattle/Arena.cs b/Testnet/HashBattle/HashBattle/Arena.cs index 8fd58dc8..8b39512f 100644 --- a/Testnet/HashBattle/HashBattle/Arena.cs +++ b/Testnet/HashBattle/HashBattle/Arena.cs @@ -7,188 +7,293 @@ /// public class Arena : SmartContract { - public Arena(ISmartContractState smartContractState) - : base(smartContractState) + private void SetBattle(ulong battleId, BattleMain battle) { - BattleOwner = Message.Sender; + State.SetStruct($"battle:{battleId}", battle); + } + public BattleMain GetBattle(ulong battleId) + { + return State.GetStruct($"battle:{battleId}"); + } + private void SetUser(ulong battleId, Address address, BattleUser user) + { + State.SetStruct($"user:{battleId}:{address}", user); + } + public BattleUser GetUser(ulong battleId, Address address) + { + return State.GetStruct($"user:{battleId}:{address}"); + } + private void SetHighestScorer(ulong battleId, BattleHighestScorer highestScorer) + { + State.SetStruct($"scorer:{battleId}", highestScorer); + } + public BattleHighestScorer GetHighestScorer(ulong battleId) + { + return State.GetStruct($"scorer:{battleId}"); + } + private void SetUserIndex(ulong battleId, uint userindex) + { + State.SetUInt32($"user:{battleId}", userindex); + } + private uint GetUserIndex(ulong battleId) + { + return State.GetUInt32($"user:{battleId}"); + } + private void SetScoreSubmittedCount(ulong battleId, uint scoresubmitcount) + { + State.SetUInt32($"scoresubmit:{battleId}", scoresubmitcount); + } + private uint GetScoreSubmittedCount(ulong battleId) + { + return State.GetUInt32($"scoresubmit:{battleId}"); } - /// /// Set the address deploying the contract as battle owner /// - private Address BattleOwner + public Address Owner + { + get => State.GetAddress(nameof(Owner)); + private set => State.SetAddress(nameof(Owner), value); + } + public Address PendingOwner + { + get => State.GetAddress(nameof(PendingOwner)); + private set => State.SetAddress(nameof(PendingOwner), value); + } + public uint MaxUsers + { + get => State.GetUInt32(nameof(MaxUsers)); + private set => State.SetUInt32(nameof(MaxUsers), value); + } + /// + /// Set the unique battleId of each battle + /// + public ulong NextBattleId { - get => PersistentState.GetAddress(nameof(BattleOwner)); - set => PersistentState.SetAddress(nameof(BattleOwner), value); + get => State.GetUInt64(nameof(NextBattleId)); + private set => State.SetUInt64(nameof(NextBattleId), value); + } + + public Arena(ISmartContractState smartContractState, uint maxUsers) : base(smartContractState) + { + Owner = Message.Sender; + MaxUsers = maxUsers; + NextBattleId = 1; } + /// + /// Only owner can set new owner and new owner will be in pending state + /// till new owner will call method. + /// + /// The new owner which is going to be in pending state + public void SetPendingOwner(Address newOwner) + { + EnsureOwnerOnly(); + PendingOwner = newOwner; + + Log(new OwnershipTransferRequestedLog { CurrentOwner = Owner, PendingOwner = newOwner }); + } + + /// + /// Waiting be called after new owner is requested by call. + /// Pending owner will be new owner after successfull call. + /// + public void ClaimOwnership() + { + var newOwner = PendingOwner; + + Assert(newOwner == Message.Sender, "ClaimOwnership must be called by the new(pending) owner."); + + var oldOwner = Owner; + Owner = newOwner; + PendingOwner = Address.Zero; + + Log(new OwnershipTransferredLog { PreviousOwner = oldOwner, NewOwner = newOwner }); + } /// /// Battle owner will start the battle /// - public bool StartBattle(ulong battleId, ulong fee) + public ulong StartBattle(ulong fee) { - Assert(Message.Sender == BattleOwner, "Only battle owner can start game."); + Assert(Message.Sender == Owner, "Only battle owner can start game."); + Assert(fee < ulong.MaxValue / MaxUsers, "Fee is too high"); + + var battleId = NextBattleId; + NextBattleId = battleId + 1; - var battle = new BattleMain(); - battle.BattleId = battleId; - battle.MaxUsers = 4; - battle.Fee = fee; - battle.Users = new Address[battle.MaxUsers]; + var battle = new BattleMain + { + BattleId = battleId, + Fee = fee, + Users = new Address[MaxUsers] + }; SetBattle(battleId, battle); - Log(battle); - return true; + Log(new BattleStartedLog { BattleId = battleId, Address = Message.Sender }); + return battleId; } - /// /// 4 different user will enter the battle /// - public bool EnterBattle(ulong battleId, uint userindex) + public void EnterBattle(ulong battleId) { var battle = GetBattle(battleId); - Assert(battle.Winner == Address.Zero, "Battle ended."); + Assert(battle.Winner == Address.Zero, "Battle not found."); - Assert(battle.Fee == Message.Value, "Battle amount is not matching."); + Assert(battle.Fee == Message.Value, "Battle fee is not matching with entry fee paid."); var user = GetUser(battleId, Message.Sender); Assert(!user.ScoreSubmitted, "The user already submitted score."); - user.Address = Message.Sender; - SetUser(battleId, Message.Sender, user); - battle.Users.SetValue(user.Address, userindex); + var userindex = GetUserIndex(battleId); + Assert(userindex != MaxUsers, "Max user reached for this battle."); + battle.Users.SetValue(Message.Sender, userindex); + SetUserIndex(battleId, userindex + 1); + SetBattle(battleId, battle); - Log(battle); - return true; + Log(new BattleEnteredLog { BattleId = battleId, Address = Message.Sender }); } - /// /// 4 different user will end the battle and submit the score /// - public bool EndBattle(Address userAddress, ulong battleId, uint score, bool IsBattleOver) + public void EndBattle(Address userAddress, ulong battleId, uint score) { - Assert(Message.Sender == BattleOwner, "Only battle owner can end game."); + Assert(Message.Sender == Owner, "Only battle owner can end game."); + + var ScoreSubmittedCount = GetScoreSubmittedCount(battleId); + Assert(ScoreSubmittedCount < MaxUsers, "All users already submitted score."); var battle = GetBattle(battleId); - Assert(battle.Winner == Address.Zero, "Battle ended."); + Assert(battle.Winner == Address.Zero, "Battle not found."); var user = GetUser(battleId, userAddress); Assert(!user.ScoreSubmitted, "The user already submitted score."); - user.Score = score; user.ScoreSubmitted = true; SetUser(battleId, userAddress, user); - if (IsBattleOver) - ProcessWinner(battle); + ScoreSubmittedCount += 1; + SetScoreSubmittedCount(battleId, ScoreSubmittedCount); - Log(user); - return true; - } + var highestScorer = GetHighestScorer(battleId); + + if (score > highestScorer.Score) + { + highestScorer.Score = score; + highestScorer.HighestScorer = userAddress; + highestScorer.HighestScoreCount = 1; + + SetHighestScorer(battleId, highestScorer); + } + else if (score == highestScorer.Score) + { + highestScorer.HighestScoreCount++; + SetHighestScorer(battleId, highestScorer); + } + if (ScoreSubmittedCount == MaxUsers) + { + highestScorer = GetHighestScorer(battleId); + if (highestScorer.HighestScoreCount > 1) + CancelBattle(battle); + else + ProcessWinner(battle, highestScorer.HighestScorer); + } + + Log(new BattleEndedLog { BattleId = battleId, Address = Message.Sender }); + } /// /// Get winner address /// public Address GetWinner(ulong battleId) { var battle = GetBattle(battleId); - Log(battle); return battle.Winner; } - /// /// Process winner when all user scores are submitted /// - private void ProcessWinner(BattleMain battle) + private void ProcessWinner(BattleMain battle, Address winnerAddress) { - if (battle.Users.Length <= 4) - { - foreach (Address userAddress in battle.Users) - { - var user = GetUser(battle.BattleId, userAddress); - if (!user.ScoreSubmitted) - return; - } - } - uint winnerIndex = GetWinnerIndex(battle.BattleId, battle.Users); - if (battle.Winner == Address.Zero) - { - battle.Winner = battle.Users[winnerIndex]; - SetBattle(battle.BattleId, battle); - ProcessPrize(battle.BattleId); - } - } - - /// - /// Get winner user index from battle users - /// - private uint GetWinnerIndex(ulong battleid, Address[] users) - { - uint winningScore = 0; - uint winningScoreIndex = 0; - for (uint i = 0; i < users.Length; i++) - { - var user = GetUser(battleid, users[i]); - if (user.Score > winningScore) - { - winningScore = user.Score; - winningScoreIndex = i; - } - } - return winningScoreIndex; + battle.Winner = winnerAddress; + SetBattle(battle.BattleId, battle); + ProcessPrize(battle); } - /// /// Send 3/4 amount to winner and 1/4 amount to battle owner /// - private void ProcessPrize(ulong battleid) + private void ProcessPrize(BattleMain battle) { - var battle = GetBattle(battleid); - ulong prize = battle.Fee * (battle.MaxUsers - 1); + var prize = battle.Fee * (MaxUsers - 1); Transfer(battle.Winner, prize); - Transfer(BattleOwner, battle.Fee); + Transfer(Owner, battle.Fee); } - - private void SetUser(ulong battleid, Address address, BattleUser user) - { - PersistentState.SetStruct($"user:{battleid}:{address}", user); - } - - private BattleUser GetUser(ulong battleid, Address address) + /// + /// Cancel battle and refund the fee amount + /// + private void CancelBattle(BattleMain battle) { - return PersistentState.GetStruct($"user:{battleid}:{address}"); - } + battle.IsCancelled = true; + SetBattle(battle.BattleId, battle); - private void SetBattle(ulong battleid, BattleMain battle) - { - PersistentState.SetStruct($"battle:{battleid}", battle); + Transfer(battle.Users[0], battle.Fee); + Transfer(battle.Users[1], battle.Fee); + Transfer(battle.Users[2], battle.Fee); + Transfer(battle.Users[3], battle.Fee); } - - private BattleMain GetBattle(ulong battleid) + private void EnsureOwnerOnly() { - return PersistentState.GetStruct($"battle:{battleid}"); + Assert(Message.Sender == Owner, "The method is owner only."); } - public struct BattleMain { public ulong BattleId; public Address Winner; public Address[] Users; - public uint MaxUsers; public ulong Fee; + public bool IsCancelled; } - public struct BattleUser { - public Address Address; - public uint Score; public bool ScoreSubmitted; } -} + public struct BattleHighestScorer + { + public uint Score; + public uint HighestScoreCount; + public Address HighestScorer; + } + public struct OwnershipTransferredLog + { + [Index] public Address PreviousOwner; + [Index] public Address NewOwner; + } + public struct OwnershipTransferRequestedLog + { + [Index] public Address CurrentOwner; + [Index] public Address PendingOwner; + } + public struct BattleStartedLog + { + [Index] public ulong BattleId; + [Index] public Address Address; + } + public struct BattleEnteredLog + { + [Index] public ulong BattleId; + [Index] public Address Address; + } + public struct BattleEndedLog + { + [Index] public ulong BattleId; + [Index] public Address Address; + } +} \ No newline at end of file diff --git a/Testnet/HashBattle/HashBattle/HashBattle.csproj b/Testnet/HashBattle/HashBattle/HashBattle.csproj index 780e51c7..2568c851 100644 --- a/Testnet/HashBattle/HashBattle/HashBattle.csproj +++ b/Testnet/HashBattle/HashBattle/HashBattle.csproj @@ -2,10 +2,10 @@ netcoreapp2.1 - - 8.0 + + 8.0 - + diff --git a/Testnet/HashBattle/HashBattleTest/ArenaTest.cs b/Testnet/HashBattle/HashBattleTest/ArenaTest.cs index 29391d2d..b2227cf1 100644 --- a/Testnet/HashBattle/HashBattleTest/ArenaTest.cs +++ b/Testnet/HashBattle/HashBattleTest/ArenaTest.cs @@ -58,79 +58,75 @@ public void TestBattle() private Arena StartBattleTest() { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); - Arena arena = new Arena(this.mockContractState.Object); - arena.StartBattle(1, 1); + Arena arena = new Arena(this.mockContractState.Object, 4); + ulong battleId = arena.StartBattle(1); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"battle:{1}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleStartedLog { BattleId = battleId, Address = this.ownerAddress })); return arena; } private void Player1EnterGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress1, 1)); - arena.EnterBattle(1, 0); + arena.EnterBattle(1); - Assert.Equal(this.playerAddress1, state.GetStruct($"user:{1}:{this.playerAddress1}").Address); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"battle:{1}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress1 })); } private void Player2EnterGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress2, 1)); - arena.EnterBattle(1, 1); + arena.EnterBattle(1); - Assert.Equal(this.playerAddress2, state.GetStruct($"user:{1}:{this.playerAddress2}").Address); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"battle:{1}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress2 })); } private void Player3EnterGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress3, 1)); - arena.EnterBattle(1, 2); + arena.EnterBattle(1); - Assert.Equal(this.playerAddress3, state.GetStruct($"user:{1}:{this.playerAddress3}").Address); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"battle:{1}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress3 })); } private void Player4EnterGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.playerAddress4, 1)); - arena.EnterBattle(1, 3); + arena.EnterBattle(1); - Assert.Equal(this.playerAddress4, state.GetStruct($"user:{1}:{this.playerAddress4}").Address); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"battle:{1}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEnteredLog { BattleId = 1, Address = this.playerAddress4 })); } private void Player1EndGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); - arena.EndBattle(this.playerAddress1, 1, 10, false); + arena.EndBattle(this.playerAddress1, 1, 10); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"user:{1}:{this.playerAddress1}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); } private void Player2EndGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); - arena.EndBattle(this.playerAddress2, 1, 20, false); + arena.EndBattle(this.playerAddress2, 1, 20); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"user:{1}:{this.playerAddress2}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); } private void Player3EndGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); - arena.EndBattle(this.playerAddress3, 1, 30, false); + arena.EndBattle(this.playerAddress3, 1, 30); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"user:{1}:{this.playerAddress3}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); } private void Player4EndGameTest(Arena arena) { this.mockContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.ownerAddress, 0)); - arena.EndBattle(this.playerAddress4, 1, 40, true); + arena.EndBattle(this.playerAddress4, 1, 40); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"user:{1}:{this.playerAddress4}"))); + this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, new BattleEndedLog { BattleId = 1, Address = this.ownerAddress })); } private void GetGameWinnerTest(Arena arena) @@ -139,7 +135,6 @@ private void GetGameWinnerTest(Arena arena) Address winner = arena.GetWinner(1); Assert.Equal(this.playerAddress4, winner); - this.mockContractLogger.Verify(m => m.Log(this.mockContractState.Object, state.GetStruct($"battle:{1}"))); } } } diff --git a/Testnet/HashBattle/HashBattleTest/InMemoryState.cs b/Testnet/HashBattle/HashBattleTest/InMemoryState.cs index 8f20e1db..c561a923 100644 --- a/Testnet/HashBattle/HashBattleTest/InMemoryState.cs +++ b/Testnet/HashBattle/HashBattleTest/InMemoryState.cs @@ -7,77 +7,82 @@ namespace HashBattleTest public class InMemoryState : IPersistentState { private readonly Dictionary storage = new Dictionary(); + public bool IsContractResult { get; set; } - public void Clear(string key) => storage.Remove(key); - public T GetValue(string key) => (T)storage.GetValueOrDefault(key, default(T)); + public void Clear(string key) => this.storage.Remove(key); + + public T GetValue(string key) => (T)this.storage.GetValueOrDefault(key, default(T)); public void AddOrReplace(string key, object value) { - if (!storage.TryAdd(key, value)) - storage[key] = value; + if (!this.storage.TryAdd(key, value)) + { + this.storage[key] = value; + } } - public Address GetAddress(string key) => GetValue
(key); - public T[] GetArray(string key) => GetValue(key); + public Address GetAddress(string key) => this.GetValue
(key); + + public T[] GetArray(string key) => this.GetValue(key); - public bool GetBool(string key) => GetValue(key); + public bool GetBool(string key) => this.GetValue(key); public byte[] GetBytes(byte[] key) => throw new NotImplementedException(); - public byte[] GetBytes(string key) => GetValue(key); + public byte[] GetBytes(string key) => this.GetValue(key); - public char GetChar(string key) => GetValue(key); + public char GetChar(string key) => this.GetValue(key); - public int GetInt32(string key) => GetValue(key); + public int GetInt32(string key) => this.GetValue(key); - public long GetInt64(string key) => GetValue(key); + public long GetInt64(string key) => this.GetValue(key); - public string GetString(string key) => GetValue(key); + public string GetString(string key) => this.GetValue(key); public T GetStruct(string key) - where T : struct => GetValue(key); + where T : struct => this.GetValue(key); - public uint GetUInt32(string key) => GetValue(key); + public uint GetUInt32(string key) => this.GetValue(key); - public ulong GetUInt64(string key) => GetValue(key); + public ulong GetUInt64(string key) => this.GetValue(key); - public UInt128 GetUInt128(string key) => GetValue(key); + public UInt128 GetUInt128(string key) => this.GetValue(key); - public UInt256 GetUInt256(string key) => GetValue(key); + public UInt256 GetUInt256(string key) => this.GetValue(key); - public bool IsContract(Address address) => IsContractResult; + public bool IsContract(Address address) => this.IsContractResult; - public void SetAddress(string key, Address value) => AddOrReplace(key, value); + public void SetAddress(string key, Address value) => this.AddOrReplace(key, value); - public void SetArray(string key, Array a) => AddOrReplace(key, a); + public void SetArray(string key, Array a) => this.AddOrReplace(key, a); - public void SetBool(string key, bool value) => AddOrReplace(key, value); + public void SetBool(string key, bool value) => this.AddOrReplace(key, value); public void SetBytes(byte[] key, byte[] value) { throw new NotImplementedException(); } - public void SetBytes(string key, byte[] value) => AddOrReplace(key, value); + public void SetBytes(string key, byte[] value) => this.AddOrReplace(key, value); - public void SetChar(string key, char value) => AddOrReplace(key, value); + public void SetChar(string key, char value) => this.AddOrReplace(key, value); - public void SetInt32(string key, int value) => AddOrReplace(key, value); + public void SetInt32(string key, int value) => this.AddOrReplace(key, value); - public void SetInt64(string key, long value) => AddOrReplace(key, value); + public void SetInt64(string key, long value) => this.AddOrReplace(key, value); - public void SetString(string key, string value) => AddOrReplace(key, value); + public void SetString(string key, string value) => this.AddOrReplace(key, value); public void SetStruct(string key, T value) - where T : struct => AddOrReplace(key, value); + where T : struct => this.AddOrReplace(key, value); - public void SetUInt32(string key, uint value) => AddOrReplace(key, value); + public void SetUInt32(string key, uint value) => this.AddOrReplace(key, value); - public void SetUInt64(string key, ulong value) => AddOrReplace(key, value); + public void SetUInt64(string key, ulong value) => this.AddOrReplace(key, value); - public void SetUInt128(string key, UInt128 value) => AddOrReplace(key, value); + public void SetUInt128(string key, UInt128 value) => this.AddOrReplace(key, value); - public void SetUInt256(string key, UInt256 value) => AddOrReplace(key, value); + public void SetUInt256(string key, UInt256 value) => this.AddOrReplace(key, value); } } diff --git a/Testnet/HashBattle/README.MD b/Testnet/HashBattle/README.MD index d6fdfb83..ea8daf98 100644 --- a/Testnet/HashBattle/README.MD +++ b/Testnet/HashBattle/README.MD @@ -6,10 +6,10 @@ v2.0.0 ``` **Contract Hash** ``` -5fce69c0bdd6e6cf7d1a3ef04c463c6bfb4066bb043dac29d90e2c0806e47c98 +07e52e71a4ce2afa30d03280b15a205b53eb7131c5fae709031fdf570d4a80b2 ``` **Contract Byte Code** ``` -4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0102009C4DC5BE0000000000000000E00022200B013000001000000002000000000000B22E0000002000000040000000000010002000000002000004000000000000000400000000000000006000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000602E00004F000000000000000000000000000000000000000000000000000000004000000C000000442E00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000B80E0000002000000010000000020000000000000000000000000000200000602E72656C6F6300000C000000004000000002000000120000000000000000000000000000400000420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000942E000000000000480000000200050030240000140A00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000660203280400000A0202280500000A6F0600000A28030000062A4602280700000A72010000706F0800000A2A4A02280700000A7201000070036F0900000A2A001330030064000000010000110202280500000A6F0600000A022802000006280A00000A7219000070280B00000A1200FE15030000021200037D0100000412001A7D040000041200047D050000041200067B040000048D070000017D03000004020306280D0000060206280100002B172A13300400B6000000020000110203280E0000060A02067B020000047E0D00000A280A00000A725D000070280B00000A02067B0500000402280500000A6F0E00000AFE017279000070280B00000A020302280500000A6F0600000A280C0000060B02077B0800000416FE0172D9000070280B00000A120102280500000A6F0600000A7D06000004020302280500000A6F0600000A07280B000006067B03000004077B060000048C07000001046E6F0F00000A020306280D0000060206280100002B172A0000133004008E000000020000110202280500000A6F0600000A022802000006280A00000A721D010070280B00000A0204280E0000060A02067B020000047E0D00000A280A00000A725D000070280B00000A020403280C0000060B02077B0800000416FE0172D9000070280B00000A1201057D070000041201177D0800000402040307280B0000060E042C07020628080000060207280200002B172A00001330020016000000010000110203280E0000060A0206280100002B067B020000042A0000133003008F00000003000011037B030000048E691A3032037B030000040B160C2B210708A3070000010D02037B0100000409280C0000067B080000042D012A0817580C08078E6932D902037B01000004037B0300000428090000060A037B020000047E0D00000A280A00000A2C2C0F01037B0300000406A3070000017D0200000402037B0100000403280D00000602037B01000004280A0000062A00133004003700000004000011160A160B160C2B2502030408A307000001280C0000060D097B07000004063609097B070000040A080B0817D70C086E048E696A32D3072A00133003003B000000050000110203280E0000060A067B05000004067B0400000417DB6ED90B02067B0200000407281000000A2602022802000006067B05000004281000000A262A8E02280700000A725D010070038C0E000001048C07000001281100000A056F0300002B2A8A02280700000A725D010070038C0E000001048C07000001281100000A6F0400002B2A7602280700000A7277010070038C0E000001281400000A046F0500002B2A7202280700000A7277010070038C0E000001281400000A6F0600002B2A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C0000007C030000237E0000E80300004403000023537472696E6773000000002C0700009001000023555300BC080000100000002347554944000000CC0800004801000023426C6F6200000000000000020000015715A201090A000000FA013300160000010000000F00000004000000080000000E000000170000001400000003000000050000000100000001000000020000000100000002000000020000000600000000009701010000000000060028015202060048015202060014013F020F00720200000A00D802BA020A00C800BA020A00AA02BA020600B200A9010A006000BA020A00EF00BA0206003103A9010600E602A9010A000103BA0206000100A90106008C01A901000000000800000000000100010001001000110000001500010001000A011000B0010000210001000F000A0110000C020000210006000F0006002E00B0000600E501580006008402B30006008102B80006005800B0000600AA0258000600BC00B80006004000BB0050200000000086183902100001006A20000000008108EC011B0002007C20000000008108FC01BE00020090200000000086009F00C400030000210000000086007F00CA000500C4210000000086007500D00007006022000000008600E201D9000B008422000000008100D401DF000C0020230000000081001803E5000D0064230000000081007F01ED000F00AB230000000081001F02F2001000CF230000000081001702FB001300F22300000000810095000301150010240000000081008B000A01170000000100DC00000001007901000001003700000002005C0000000100370000000200270300000100900200000200370000000300C200000004002C0200000100370000000100AB00000001004F00000002008A02000001004F00000001004F0000000200B202000003002702000001004F0000000200B202000001004F0000000200AB00000001004F00090039020100110039020600190039020A002900390210002900690016004900C0011B0029000001200051009C0225005100A7022B00390037033700290011033F002900930145003900BB015800490066015C005900700160002900CB0183007900D1028B005100F70292005100ED029A007900D102A1002E000B0015012E0013001E012E001B003D01320051006B0075007D0002000100000000021001020002000300010003000300048000000000000000000000000000000000D8020000040000000000000000000000A7001700000000000200000000000000000000000000BA0200000000030002000400020019004C0019006600250066002700660025004C0027004C0000000055496E743634003C4D6F64756C653E004172656E610053797374656D2E507269766174652E436F72654C696200426174746C65496400626174746C6549640053636F72655375626D697474656400626174746C656964004665650066656500494D657373616765006765745F4D65737361676500456E64426174746C6500456E746572426174746C6500476574426174746C6500536574426174746C65005374617274426174746C6500626174746C650056616C7565547970650053636F72650073636F72650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E745374617465006765745F50657273697374656E7453746174650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465006765745F56616C75650053657456616C75650076616C75650050726F636573735072697A6500537472696E67004C6F6700536D617274436F6E74726163742E646C6C0053797374656D00426174746C654D61696E005A65726F006765745F53656E646572005472616E736665720050726F6365737357696E6E65720047657457696E6E6572006765745F426174746C654F776E6572007365745F426174746C654F776E657200426174746C6555736572004765745573657200536574557365720075736572004973426174746C654F766572002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004D6178557365727300757365727300757365724164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300466F726D617400536D617274436F6E7472616374004F626A656374004765745374727563740053657453747275637400495472616E73666572526573756C74004173736572740047657457696E6E6572496E6465780075736572696E646578004172726179006F705F457175616C6974790000001742006100740074006C0065004F0077006E006500720000434F006E006C007900200062006100740074006C00650020006F0077006E00650072002000630061006E002000730074006100720074002000670061006D0065002E00001B42006100740074006C006500200065006E006400650064002E00005F42006100740074006C006500200066006500650020006900730020006E006F00740020006D00610074006300680069006E00670020007700690074006800200065006E007400720079002000660065006500200070006100690064002E00004354006800650020007500730065007200200061006C007200650061006400790020007300750062006D00690074007400650064002000730063006F00720065002E00003F4F006E006C007900200062006100740074006C00650020006F0077006E00650072002000630061006E00200065006E0064002000670061006D0065002E00001975007300650072003A007B0030007D003A007B0031007D00001562006100740074006C0065003A007B0030007D000000000053FFE06AD37C28419B1B527317B313CF000420010108032000010520010111110520010112190420001225042000111D0420001229052001111D0E062002010E111D040701110C07000202111D111D05200201020E06300101011E00040A01110C060702110C11100306111D0320000B052002011C0A040A011110090704091D111D08111D0707040909091110050702110C0B0720021235111D0B0600030E0E1C1C07300102010E1E00063001011E000E0500020E0E1C087CEC85D7BEA7798E02060B04061D111D02060902060205200101111D052002020B0B052002020B0908200402111D0B0902052001111D0B05200101110C072002090B1D111D042001010B082003010B111D111007200211100B111D062002010B110C052001110C0B042800111D0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301080100020000000000000000000000000000000000000010000000000000000000000000000000882E00000000000000000000A22E0000002000000000000000000000000000000000000000000000942E0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000B43E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010200E6EB4ACA0000000000000000E00022200B013000001A000000020000000000001E380000002000000040000000000010002000000002000004000000000000000400000000000000006000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000CC3700004F000000000000000000000000000000000000000000000000000000004000000C000000B03700001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E746578740000002418000000200000001A000000020000000000000000000000000000200000602E72656C6F6300000C0000000040000000020000001C0000000000000000000000000000400000420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003800000000000048000000020005000C270000A410000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007602280500000A7201000070038C0B000001280600000A046F0100002B2A7202280500000A7201000070038C0B000001280600000A6F0200002B2A8E02280500000A7217000070038C0B000001048C06000001280900000A056F0300002B2A8A02280500000A7217000070038C0B000001048C06000001280900000A6F0400002B2A7602280500000A7231000070038C0B000001280600000A046F0500002B2A7202280500000A7231000070038C0B000001280600000A6F0600002B2A7602280500000A7247000070038C0B000001280600000A046F0A00000A2A7202280500000A7247000070038C0B000001280600000A6F0B00000A2A7602280500000A7259000070038C0B000001280600000A046F0A00000A2A7202280500000A7259000070038C0B000001280600000A6F0B00000A2A4602280500000A72790000706F0C00000A2A4A02280500000A7279000070036F0D00000A2A4602280500000A72850000706F0C00000A2A4A02280500000A7285000070036F0D00000A2A4602280500000A729F0000706F0B00000A2A4A02280500000A729F000070036F0A00000A2A4602280500000A72B10000706F0E00000A2A4A02280500000A72B1000070036F0F00000A2AA20203281000000A0202281100000A6F1200000A280C0000060204281000000602176A28120000062A13300300320000000100001102281D0000060203280E000006021200FE1507000002120002280B0000067D0C0000041200037D0D00000406280700002B2A0000133003005C0000000200001102280D0000060A020602281100000A6F1200000A281400000A72CB000070281500000A02280B0000060B0206280C000006027E1600000A280E000006021202FE15060000021202077D0A0000041202067D0B00000408280800002B2A13300400A9000000030000110202281100000A6F1200000A02280B000006281400000A723D010070281500000A0203156A02280F0000066E5CFE057281010070281500000A0228110000060A0206176AD728120000061202FE15030000021202067D010000041202037D04000004120202280F0000068D060000017D03000004080B0206072801000006021203FE15080000021203067D0E000004120302281100000A6F1200000A7D0F00000409280900002B062A00000013300400F300000004000011020328020000060A02067B020000047E1600000A281400000A72A1010070281500000A02067B0400000402281100000A6F1700000AFE0172C5010070281500000A020302281100000A6F1200000A28040000060B02077B0600000416FE017225020070281500000A020302281100000A6F1200000A072803000006020328080000060C020802280F000006FE0116FE017269020070281500000A067B0300000402281100000A6F1200000A8C06000001086E6F1800000A02030817D728070000060203062801000006021203FE15090000021203037D10000004120302281100000A6F1200000A7D1100000409280A00002B2A001330040045010000050000110202281100000A6F1200000A02280B000006281400000A72AD020070281500000A0204280A0000060A020602280F000006FE0572ED020070281500000A020428020000060B02077B020000047E1600000A281400000A72A1010070281500000A02040328040000060C02087B0600000416FE017225020070281500000A1202177D060000040204030828030000060617D70A0204062809000006020428060000060D05097B0700000436221203057D070000041203037D090000041203177D0800000402040928050000062B1D05097B07000004331412037C08000004254B17D75402040928050000060602280F0000063327020428060000060D097B080000041736090207281C0000062B0D0207097B09000004281A000006021204FE150A0000021204047D12000004120402281100000A6F1200000A7D130000041104280B00002B2A36020328020000067B020000042A760F01047D0200000402037B010000040328010000060203281B0000062A000000133003003300000006000011037B0400000402280F00000617DB6ED90A02037B0200000406281900000A260202280B000006037B04000004281900000A262A00133003007A000000000000000F01177D0500000402037B0100000403280100000602037B0300000416A306000001037B04000004281900000A2602037B0300000417A306000001037B04000004281900000A2602037B0300000418A306000001037B04000004281900000A2602037B0300000419A306000001037B04000004281900000A262A8A0202281100000A6F1200000A02280B000006281400000A7233030070281500000A2A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C000000EC050000237E0000580600001C05000023537472696E677300000000740B00006803000023555300DC0E0000100000002347554944000000EC0E0000B801000023426C6F6200000000000000020000015715A201090A000000FA01330016000001000000100000000A000000130000001D00000022000000190000000D000000060000000100000004000000080000000100000002000000080000000B00000000004902010000000000060068019D03060097019D03060054018A030F00BD0300000A00450427040A00170427040A001C0127040600FC005B020A00880127040A0043012704060022005B020600DB015B02060053045B020A009D0027040600FA045B020A006E042704000000002900000000000100010001001000320000001500010001000A01100062020000210001001E000A01100064030000210006001E000A01100020030000210007001E000A0110000202000021000A001E000A0110002B02000021000C001E000A0110001A02000021000E001E000A011000F1010000210010001E000A011000E2010000210012001E0006006800F2000600A6029B000600E903F50006009500F20006007A00FA0006008600FA0006000601FD000600AC04FD00060048039B000600F3029B0006000E039B00060001039B000600E6029B0006006800F200060017049B0006006800F200060017049B0006006800F200060017049B005020000000008100DF00000101006E20000000008600D500070103008B2000000000810077030D010400AF200000000086006F0316010700D22000000000810045031E010900F020000000008600340325010B000D21000000008100E3042B010C002B21000000008100D60431010E00482100000000810095042B010F0066210000000081007E04310111008321000000008608AD026E0012009521000000008108B70236011200A821000000008608C1026E001300BA21000000008108D20236011300CD21000000008608CC033C011400DF21000000008108D90340011400F2210000000086084F00BD0015000422000000008108600045011500172200000000861884034A0116004022000000008600E302360118008022000000008600720206001900E822000000008600E90051011900A023000000008600C90045011A00A024000000008600B20056011B00F125000000008600A3025E011E00FF25000000008100950264011F002026000000008100CE016C0121006026000000008100BC006C012200E62600000000810000050600230000000100710000000200F500000001007100000001007100000002001F04000003007F03000001007100000002001F0400000100710000000200560300000100710000000100710000000200F00400000100710000000100710000000200BE0400000100710000000100C80100000100C80100000100C80100000100C80100000100300100000200E60300000100170300000100990000000100710000000100FD03000002007100000003000C0100000100710000000100F50000000200EF0300000100F50000000100F500090084030100110084030600190084030A0049008403060029001201100061003E041500510064041B0051005A04280061003E042F0051000B004000510001004600510009044B0051001404510051001500580051001F005D002900840363002900A6006900710081026E00290045027800310010058D002900CF04950031006D029B007100B501BD007900BF01C10029008C02E1002E000B007F012E00130088012E001B00A70141012300B00161012300B00181012300B001A1012300B001C1012300B001E1012300B00101022300B00121022300B00141022300B00161022300B00173008400A400B300CC00DD000200010000001A0372010000E60272010000DD037701000064007B0102000B00030001000C00030002000D00050001000E00050002000F00070001001000070002001100090001001200090004800000000000000000000000000000000045040000040000000000000000000000E9003800000000000200000000000000000000000000270400000000030002000400020005000200060002000700020008000200090002000A0002000F002300110023000F003600110036000F003B0011003B0027007F0027009F002700AE002700C7002700D800000000000047657455496E7433320053657455496E7433320047657455496E7436340053657455496E743634003C4D6F64756C653E004172656E610053797374656D2E507269766174652E436F72654C6962006765745F4E657874426174746C654964007365745F4E657874426174746C65496400626174746C65496400497343616E63656C6C65640053636F72655375626D6974746564004665650066656500494D657373616765006765745F4D65737361676500456E64426174746C650043616E63656C426174746C6500456E746572426174746C6500476574426174746C6500536574426174746C65005374617274426174746C6500626174746C650056616C7565547970650053636F72650073636F7265006765745F53746174650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E7453746174650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E6465784174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465006765745F56616C75650053657456616C75650076616C75650050726F636573735072697A6500537472696E6700426174746C65456E6465644C6F6700426174746C65456E74657265644C6F67004F776E6572736869705472616E736665727265644C6F6700426174746C65537461727465644C6F67004F776E6572736869705472616E736665725265717565737465644C6F6700536D617274436F6E74726163742E646C6C0053797374656D00426174746C654D61696E005A65726F00436C61696D4F776E657273686970006765745F53656E646572005472616E736665720050726F6365737357696E6E65720047657457696E6E6572006765745F4F776E6572007365745F4F776E6572006765745F50656E64696E674F776E6572007365745F50656E64696E674F776E65720053657450656E64696E674F776E65720050726576696F75734F776E65720043757272656E744F776E6572004E65774F776E6572006E65774F776E657200426174746C654869676865737453636F726572004765744869676865737453636F726572005365744869676865737453636F726572006869676865737453636F72657200426174746C6555736572004765745573657200536574557365720075736572002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573006765745F4D61785573657273007365745F4D61785573657273006D617855736572730077696E6E65724164647265737300757365724164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300466F726D617400536D617274436F6E7472616374004F626A656374004765745374727563740053657453747275637400495472616E73666572526573756C740047657453636F72655375626D6974746564436F756E740053657453636F72655375626D6974746564436F756E74004869676865737453636F7265436F756E740073636F72657375626D6974636F756E74004173736572740047657455736572496E6465780053657455736572496E6465780075736572696E64657800417272617900456E737572654F776E65724F6E6C79006F705F457175616C69747900001562006100740074006C0065003A007B0030007D00001975007300650072003A007B0030007D003A007B0031007D000015730063006F007200650072003A007B0030007D00001175007300650072003A007B0030007D00001F730063006F00720065007300750062006D00690074003A007B0030007D00000B4F0077006E00650072000019500065006E00640069006E0067004F0077006E006500720000114D00610078005500730065007200730000194E0065007800740042006100740074006C00650049006400007143006C00610069006D004F0077006E0065007200730068006900700020006D007500730074002000620065002000630061006C006C0065006400200062007900200074006800650020006E00650077002800700065006E00640069006E006700290020006F0077006E00650072002E0000434F006E006C007900200062006100740074006C00650020006F0077006E00650072002000630061006E002000730074006100720074002000670061006D0065002E00001F460065006500200069007300200074006F006F0020006800690067006800002342006100740074006C00650020006E006F007400200066006F0075006E0064002E00005F42006100740074006C006500200066006500650020006900730020006E006F00740020006D00610074006300680069006E00670020007700690074006800200065006E007400720079002000660065006500200070006100690064002E00004354006800650020007500730065007200200061006C007200650061006400790020007300750062006D00690074007400650064002000730063006F00720065002E0000434D00610078002000750073006500720020007200650061006300680065006400200066006F00720020007400680069007300200062006100740074006C0065002E00003F4F006E006C007900200062006100740074006C00650020006F0077006E00650072002000630061006E00200065006E0064002000670061006D0065002E00004541006C006C00200075007300650072007300200061006C007200650061006400790020007300750062006D00690074007400650064002000730063006F00720065002E00003354006800650020006D006500740068006F00640020006900730020006F0077006E006500720020006F006E006C0079002E000000F641A50CB4EA5F4D9C24BB4EACC176CA0004200101080320000105200101111104200012290500020E0E1C07300102010E1E00040A01110C063001011E000E0600030E0E1C1C040A011110040A011114052002010E09042001090E05200111190E062002010E11190420010B0E052002010E0B05200101121D04200012390420001119040701111C06300101011E00040A01111C080703111911191118070002021119111905200201020E03061119040A0111180907040B110C110C1120040A011120090704110C11100911240320000B052002011C0A040A0111240B070509110C111011141128040A0111280307010B072002124111190B087CEC85D7BEA7798E02060B04061D1119020602020609062002010B110C052001110C0B082003010B1119111007200211100B1119062002010B111405200111140B052002010B09042001090B052001011119032000090420010109042001010B06200201121D090420010B0B0720030111190B0905200111190B07200201110C111905200101110C0428001119032800090328000B0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301080100020000000000040100000000000000000000000000000000000010000000000000000000000000000000F437000000000000000000000E38000000200000000000000000000000000000000000000000000000380000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000C000000203800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ```