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
```