diff --git a/.gitignore b/.gitignore index 8d1a377..ad2d3a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,165 +1,25 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates - -# Build results - -[Dd]ebug/ -[Rr]elease/ -x64/ -build/ [Bb]in/ [Oo]bj/ +[Ww]orking*/ +Build/Temp/ +Doc/doc.shfbproj_* -# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets -!packages/*/build/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -*_i.c -*_p.c -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.log -*.scc - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -*.ncrunch* -.*crunch*.local.xml - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ +TestResults/ -# Publish Web Output -*.Publish.xml +**/packages/* +!**/packages/build/ -# NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -#packages/ - -# Windows Azure Build Output -csx -*.build.csdef - -# Windows Store app package directory -AppPackages/ - -# Others -sql/ -*.Cache -ClientBin/ -[Ss]tyle[Cc]op.* -~$* -*~ -*.dbmdl -*.[Pp]ublish.xml -*.pfx -*.publishsettings - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -App_Data/*.mdf -App_Data/*.ldf - -#RiderC# -.idea/ -*.*.iml - -#LightSwitch generated files -GeneratedArtifacts/ -_Pvt_Extensions/ -ModelManifest.xml - -# ========================= -# Windows detritus -# ========================= - -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Mac desktop service store files -.DS_Store - - -# Sandcasle Output Folder -Help/* -/.vs/IotaApi -/packages +BenchmarkDotNet.Artifacts/ +*.suo +*.user +*.userprefs +_ReSharper.* +*.ReSharper.user +*.resharper.user +.vs/ +.vscode/ +*.lock.json +*.nuget.props +*.nuget.targets +*.orig +.DS_Store \ No newline at end of file diff --git a/IotaApi.Standard.Tests/IotaApi.Standard.Tests.csproj b/IotaApi.Standard.Tests/IotaApi.Standard.Tests.csproj deleted file mode 100644 index 895f697..0000000 --- a/IotaApi.Standard.Tests/IotaApi.Standard.Tests.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - netcoreapp2.0 - - false - - Iota.Api.Standard.Tests - - - - - - - - - - - - - - diff --git a/IotaApi.Standard.Tests/IotaApiTests.cs b/IotaApi.Standard.Tests/IotaApiTests.cs deleted file mode 100644 index 3870ded..0000000 --- a/IotaApi.Standard.Tests/IotaApiTests.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Collections.Generic; -using Iota.Api.Standard.Exception; -using Iota.Api.Standard.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests -{ - [TestClass] - public class IotaApiTests - { - private static readonly string TEST_SEED1 = - "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_1 = - "MALAZGDVZIAQQRTNYJDSZMY9VE9LAHQKTVCUOAGZUCX9IBUMODFFTMGUIUAXGLWZQ9CYRSLYBM9QBIBYAEIAOPKXEA"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2 = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_3 = - "ASCZZOBQDMNHLELQKWJBMRETMHBTF9V9TNKYDIFW9PDXPUHPVVGHMSWPVMNJHSJF99QFCMNTPCPGS9DT9XAFKJVO9X"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_1 = - "MALAZGDVZIAQQRTNYJDSZMY9VE9LAHQKTVCUOAGZUCX9IBUMODFFTMGUIUAXGLWZQ9CYRSLYBM9QBIBYA"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2 = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZC"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_3 = - "ASCZZOBQDMNHLELQKWJBMRETMHBTF9V9TNKYDIFW9PDXPUHPVVGHMSWPVMNJHSJF99QFCMNTPCPGS9DT9"; - - private static readonly string TEST_SEED2 = - "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - - private static readonly string TEST_HASH = - "9XWWWXVQYPKLVMAMFPXFSE9UCAGVY9RZO9NHGAZEXIRIJRZULGMFOJNDKUNFUCSURWRDDPVMYG9X99999"; //06/02/2018,04:36 - - private static readonly string TEST_TRYTES = - "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCQ9CA9TA99999999999999999999XC9999999999999999999999999TPLCUYD99999999999B99999999WFVTNIRLVFIVKAQEEDFRWWLXIPHRQNG9EAY9QEWRFDLECXDGJLIKBAKBYPTAZPISWVXJLBJISGGLWTBVDNBEBXBG9PZHPK9SVNH99LZVXYZSVODZZIIXNJJQAYXCNKISVFVXGVQMURVEMSDGLRLZADQCOHRHW99999K9SYJTSNRZVYWGSV9AXVTPKMTLHPCTIJGNNAMALVPQUCGCZXZFFUQSXCHPSJLXBADVOIZO9PSZYTA9999XC9999999999999999999999999WWF9MWCJE999999999MMMMMMMMMPRQAGSHN9ZHEWVAANNPXSDRRROY"; - - private static readonly string TEST_MESSAGE = "COTA"; - private static readonly string TEST_TAG = "COTASPAM9999999999999999999"; - - // ReSharper disable once InconsistentNaming - private static readonly string[] TEST_ADDRESSES = - { - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC", - "P9UDUZMN9DEXCRQEKLJYSBSBZFCHOBPJSDKMLCCVJDOVOFDWMNBZRIRRZJGINOUMPJBMYYZEGRTIDUABD", - "MIMVJEYREIIZLXOXQROMPJFCIX9NFVXD9ZQMNZERPI9OJIJFUWQ9WCTMKXEEPHYPWEZPHLJBJOFH9YTRB", - "FOJHXRVJRFMJTFDUWJYYZXCZIJXKQALLXMLKHZFDMHWTIBBXUKSNSUYJLKYRQBNXKRSUXZHDTPWXYD9YF", - "B9YNPQO9EXID9RDEEGLCBJBYKBLWHTOQOZKTLJDFPJZOPKJJTNUYUVVTDJPBCBYIWGPSCMNRZFGFHFSXH", - "NQEFOAFIYKZOUXDFQ9X9PHCNSDETRTJZINZ9EYGKU99QJLDSTSC9VTBAA9FHLNLNYQXWLTNPRJDWCGIPP", - "CEGLBSXDJVXGKGOUHRGMAQDRVYXCQLXBKUDWKFFSIABCUYRATFPTEEDIFYGAASKFZYREHLBIXBTKP9KLC", - "QLOXU9GIQXPPE9UUT9DSIDSIESRIXMTGZJMKLSJTNBCRELAVLWVJLUOLKGFCWAEPEQWZWPBV9YZJJEHUS", - "XIRMYJSGQXMM9YPHJVVLAVGBBLEEMOOKHHBFWKEAXJFONZLNSLBCGPQEVDMMOGHFVRDSYTETIFOIVNCR9", - "PDVVBYBXMHZKADPAYOKQNDPHRSWTHAWQ9GRVIBOIMZQTYCWEPCDWDVRSOUNASVBDLBOAMVLYEVVCMAM9N", - "U9GAIAPUUQWJGISAZWPLHUELTZ9WSHWXS9JLPKOWHRRIVUKGWCTJMBULVMKTETTUNHZ9HWHBALUCJIROU", - "VFPMKZLLMDUOEKNBEKQZPTNZJZF9UHRWSTHXLWQQ9OAXTZQHTZPAWNJNXKAZFSDFWKFQEKZIGJTLWQFLO", - "IGHK9XIWOAYBZUEZHQLEXBPTXSWVANIOUZZCPNKUIJIJOJNAQCJWUJHYKCZOIKVAAHDGAWJZKLTPVQL9G", - "LXQPWMNXSUZTEYNC9ZBBFHY9YWCCOVKBNIIOUSVXZJZMJKJFDUWGUVXYCHGKUHEEIDHSGEWFAHVJPRIJT", - "AKFDX9PGGQLZUWRMZ9YBDF9CG9TWXCNALCSXSAWHFIMGXCSYCJLSWIQDGGVDRMNEKKECQEYAITGNLNJFQ", - "YX9QSPYMSFVOW9UVZRDVOCPYYMUTDHCCPKHMXQSJQJYIXVCHILKW9GBYJTYGLIKBTRQMDCYBMLLNGSSIK", - "DSYCJKNG9TAGJHSKZQ9XLKAKNSKJFZIPVEDGJFXRTFGENHZFQGXHWDBNXLLDABDMOYELPG9DIXSNJFWAR", - "9ANNACZYLDDPZILLQBQG9YMG9XJUMTAENDFQ9HMSSEFWYOAXPJTUXBFTSAXDJPAO9FKTWBBSCSFMOUR9I", - "WDTFFXHBHMFQQVXQLBFJFVVHVIIAVYM9PFAZCHMKET9ESMHIRHSMVDJBZTXPTAFVIASMSXRDCIYVWVQNO", - "XCCPS9GMTSUB9DXPVKLTBDHOFX9PJMBYZQYQEXMRQDPGQPLWRGZGXODYJKGVFOHHYUJRCSXAIDGYSAWRB", - "KVEBCGMEOPDPRCQBPIEMZTTXYBURGZVNH9PLHKPMM9D9FUKWIGLKZROGNSYIFHULLWQWXCNAW9HKKVIDC" - }; - - private static readonly string TestTrytesValid = - "JUSTANOTHERTEST999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTH999999999999999999999999999COTASPAM9999999999999999999VISYF9DGE999999999999999999FB9CRHGOHK9EIDHDUWSGDDONYQAABTRXXMFUKRZHMVJAPCAADTRDCWZJRHAPL9LRIVZFVKQV9GAWSSJZDPWGPQTPWCPNYONYGGSJLJAQYXLZ9FMOTUJT9RIXAOXFDQZSTZYBCHSNLSM9JAXTMNQBUHAAZIIR999999PWGPQTPWCPNYONYGGSJLJAQYXLZ9FMOTUJT9RIXAOXFDQZSTZYBCHSNLSM9JAXTMNQBUHAAZIIR999999KXOQZNGXOCACOVYKPWWJFQQMEWDQVUZRI99WFQEJANSOPVLZGQHLUEYKPYPMSTLDRDVEBMCQMKQLL9JFS"; - - private static int MIN_WEIGHT_MAGNITUDE = 14; - private static int DEPTH = 9; - - private IotaApi _iotaClient; - - [TestInitialize] - public void CreateApiClientInstance() - { - _iotaClient = new IotaApi("node.iotawallet.info", 14265); - } - - [TestMethod] - public void ShouldGetInputs() - { - var res = _iotaClient.GetInputs(TEST_SEED1, 2, 0, 0, 0); - Console.WriteLine(res); - Assert.IsNotNull(res); - Assert.IsNotNull(res.TotalBalance); - Assert.IsNotNull(res.InputsList); - } - - [TestMethod] - public void ShouldCreateANewAddressWithChecksum() - { - // ReSharper disable RedundantArgumentDefaultValue - var res1 = _iotaClient.GetNewAddress(TEST_SEED1, 1, 0, true, 5, false); - Assert.AreEqual(res1[0], TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_1); - - var res2 = _iotaClient.GetNewAddress(TEST_SEED1, 2, 0, true, 5, false); - Assert.AreEqual(res2[0], TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2); - - var res3 = _iotaClient.GetNewAddress(TEST_SEED1, 3, 0, true, 5, false); - Assert.AreEqual(res3[0], TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_3); - // ReSharper restore RedundantArgumentDefaultValue - } - - [TestMethod] - public void ShouldCreateANewAddressWithoutChecksum() - { - // ReSharper disable RedundantArgumentDefaultValue - var res1 = _iotaClient.GetNewAddress(TEST_SEED1, 1, 0, false, 5, false); - Assert.AreEqual(res1[0], TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_1); - - var res2 = _iotaClient.GetNewAddress(TEST_SEED1, 2, 0, false, 5, false); - Assert.AreEqual(res2[0], TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2); - - var res3 = _iotaClient.GetNewAddress(TEST_SEED1, 3, 0, false, 5, false); - Assert.AreEqual(res3[0], TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_3); - // ReSharper restore RedundantArgumentDefaultValue - } - - [TestMethod] - public void ShouldCreate100Addresses() - { - // ReSharper disable RedundantArgumentDefaultValue - var res = _iotaClient.GetNewAddress(TEST_SEED1, 2, 0, false, 100, false); - Assert.AreEqual(res.Length, 100); - // ReSharper restore RedundantArgumentDefaultValue - } - - [TestMethod] - [ExpectedException(typeof(NotEnoughBalanceException))] - public void ShouldPrepareTransfer() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 100, TEST_MESSAGE, TEST_TAG), - }; - - var trytes = _iotaClient.PrepareTransfers(TEST_SEED1, 2, transfers.ToArray(), null, null, false); - - Assert.IsNotNull(trytes); - Assert.IsFalse(trytes.Count == 0); - - } - - //seed contains 0 balance - [TestMethod] - [ExpectedException(typeof(NotEnoughBalanceException))] - public void ShouldPrepareTransferWithInputs() - { - List inputlist = new List(); - List transfers = new List(); - - var inputs = _iotaClient.GetInputs(TEST_SEED1, 2, 0, 0, 0); - - inputlist.AddRange(inputs.InputsList); - - transfers.Add(new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 100, TEST_MESSAGE, TEST_TAG)); - List trytes = - _iotaClient.PrepareTransfers(TEST_SEED1, 2, transfers.ToArray(), null, inputlist, true); - - Assert.IsNotNull(trytes); - Assert.IsFalse(trytes.Count == 0); - } - - - [TestMethod] - public void ShouldGetLastInclusionState() - { - var res = _iotaClient.GetLatestInclusion(new[] {TEST_HASH}); - Assert.IsNotNull(res.States); - } - - [TestMethod] - public void ShouldFindTransactionObjects() - { - var ftr = _iotaClient.FindTransactionObjects(TEST_ADDRESSES); - Assert.IsNotNull(ftr); - } - - [TestMethod] - public void ShouldGetAccountData() - { - var accountData = _iotaClient.GetAccountData(TEST_SEED1, 2, 0, true, 0, true, 0, 0, true, 0); - Assert.IsNotNull(accountData); - } - - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void ShouldNotGetBundle() - { - var bundle = _iotaClient.GetBundle("SADASD"); - Assert.IsNotNull(bundle); - } - - [TestMethod] - public void ShouldGetBundle() - { - var bundle = _iotaClient.GetBundle(TEST_HASH); - Assert.IsNotNull(bundle); - } - - [TestMethod] - public void ShouldGetTransfers() - { - // ReSharper disable RedundantArgumentDefaultValue - var gtr = _iotaClient.GetTransfers(TEST_SEED1, 2, 0, 0, false); - // ReSharper restore RedundantArgumentDefaultValue - - foreach (var b in gtr) Assert.IsTrue(b.Transactions.TrueForAll(t => t != null)); - } - - [Ignore] - [TestMethod] - public void ShouldReplayBundle() - { - var replayedList = _iotaClient.ReplayBundle(TEST_HASH, DEPTH, MIN_WEIGHT_MAGNITUDE); - Assert.IsNotNull(replayedList); - } - - [Ignore] - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void ShouldNotSendTrytes() - { - _iotaClient.SendTrytes(new[] {TEST_TRYTES}, 9); - } - - [TestMethod] - public void ShouldGetTrytes() - { - _iotaClient.GetTrytes(TEST_HASH); - } - - [TestMethod] - public void ShouldBroadcastAndStore() - { - _iotaClient.BroadcastAndStore(new List {TEST_TRYTES}); - } - - [Ignore] - [TestMethod] - public void ShouldSendTrytes() - { - _iotaClient.SendTrytes(new[] {TestTrytesValid}, 9); - } - - [Ignore] - [TestMethod] - [ExpectedException(typeof(IllegalStateException))] - public void ShouldNotSendTransfer() - { - Transfer[] transfers = - { - new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 2, TEST_MESSAGE, TEST_TAG) - }; - - var result = _iotaClient.SendTransfer(TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers, null, null, - false, true); - Assert.IsNotNull(result); - } - - [Ignore] - [TestMethod] - public void ShouldSendTransferWithoutInputs() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 1, "JUSTANOTHERTEST", TEST_TAG) - }; - - var str = _iotaClient.SendTransfer(TEST_SEED2, 2, 9, 14, transfers.ToArray(), null, null, false, true); - - Assert.IsNotNull(str); - } - - [Ignore] - [TestMethod] - public void ShouldSendTransferWithInputs() - { - List inputlist = new List(); - List transfers = new List(); - - var inputs = _iotaClient.GetInputs(TEST_SEED1, 2, 0, 0, 1); - - inputlist.AddRange(inputs.InputsList); - - transfers.Add(new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 1, TEST_MESSAGE, TEST_TAG)); - - var str = _iotaClient.SendTransfer(TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers.ToArray(), - inputlist.ToArray(), null, - true, true); - - Assert.IsNotNull(str); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard.Tests/IotaCoreApiTest.cs b/IotaApi.Standard.Tests/IotaCoreApiTest.cs deleted file mode 100644 index 5e75e72..0000000 --- a/IotaApi.Standard.Tests/IotaCoreApiTest.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System.Linq; -using Iota.Api.Standard.Exception; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests -{ - [TestClass] - public class IotaCoreApiTest - { - private static readonly string TEST_BUNDLE = - "XZKJUUMQOYUQFKMWQZNTFMSS9FKJLOEV9DXXXWPMQRTNCOUSUQNTBIJTVORLOQPLYZOTMLFRHYKMTGZZU"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM = - "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA"; - - private static readonly string TEST_HASH = - "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999"; - - private static IotaApi _iotaApi; - - [TestInitialize] - public void CreateProxyInstance() - { - _iotaApi = new IotaApi("node.iotawallet.info", 14265); - } - - [TestMethod] - public void ShouldGetNodeInfo() - { - var nodeInfo = _iotaApi.GetNodeInfo(); - Assert.IsNotNull(nodeInfo.AppVersion); - Assert.IsNotNull(nodeInfo.AppName); - Assert.IsNotNull(nodeInfo.JreVersion); - Assert.IsNotNull(nodeInfo.JreAvailableProcessors); - Assert.IsNotNull(nodeInfo.JreFreeMemory); - Assert.IsNotNull(nodeInfo.JreMaxMemory); - Assert.IsNotNull(nodeInfo.JreTotalMemory); - Assert.IsNotNull(nodeInfo.LatestMilestone); - Assert.IsNotNull(nodeInfo.LatestMilestoneIndex); - Assert.IsNotNull(nodeInfo.LatestSolidSubtangleMilestone); - Assert.IsNotNull(nodeInfo.LatestSolidSubtangleMilestoneIndex); - Assert.IsNotNull(nodeInfo.Neighbors); - Assert.IsNotNull(nodeInfo.PacketsQueueSize); - Assert.IsNotNull(nodeInfo.Time); - Assert.IsNotNull(nodeInfo.Tips); - Assert.IsNotNull(nodeInfo.TransactionsToRequest); - } - - [TestMethod] - public void ShouldGetNeighbors() - { - var neighbors = _iotaApi.GetNeighbors(); - Assert.IsNotNull(neighbors.Neighbors); - } - - [TestMethod] - public void ShouldAddNeighbors() - { - try - { - var res = _iotaApi.AddNeighbors("udp://8.8.8.8:14265"); - Assert.IsNotNull(res); - } - catch (IotaApiException e) - { - Assert.IsTrue(e.Message.Contains("not available on this node")); - } - } - - [TestMethod] - public void ShouldRemoveNeighbors() - { - try - { - var res = _iotaApi.RemoveNeighbors("udp://8.8.8.8:14265"); - Assert.IsNotNull(res); - } - catch (IotaApiException e) - { - Assert.IsTrue(e.Message.Contains("not available on this node")); - } - } - - [TestMethod] - public void ShouldGetTips() - { - var tips = _iotaApi.GetTips(); - Assert.IsNotNull(tips); - } - - [TestMethod] - public void ShouldFindTransactionsByAddresses() - { - var trans = _iotaApi.FindTransactionsByAddresses(TEST_ADDRESS_WITH_CHECKSUM); - Assert.IsNotNull(trans.Hashes); - Assert.IsTrue(trans.Hashes.Count > 0); - } - - [TestMethod] - public void ShouldFindTransactionsByApprovees() - { - var trans = _iotaApi.FindTransactionsByApprovees(TEST_HASH); - Assert.IsNotNull(trans.Hashes); - } - - [TestMethod] - public void ShouldFindTransactionsByBundles() - { - var trans = _iotaApi.FindTransactionsByBundles(TEST_HASH); - Assert.IsNotNull(trans.Hashes); - } - - [TestMethod] - public void ShouldFindTransactionsByDigests() - { - var trans = _iotaApi.FindTransactionsByDigests(TEST_HASH); - Assert.IsNotNull(trans.Hashes); - } - - [TestMethod] - public void ShouldGetTrytes() - { - var res = _iotaApi.GetTrytes(TEST_HASH); - Assert.IsNotNull(res.Trytes); - } - - [TestMethod] - [ExpectedException(typeof(IotaApiException), "One of the tips absents")] - public void ShouldNotGetInclusionStates() - { - var res = _iotaApi.GetInclusionStates(new[] {TEST_HASH}, - new[] {"DNSBRJWNOVUCQPILOQIFDKBFJMVOTGHLIMLLRXOHFTJZGRHJUEDAOWXQRYGDI9KHYFGYDWQJZKX999999"}); - Assert.IsNotNull(res.States); - } - - [TestMethod] - public void ShouldGetInclusionStates() - { - var res = - _iotaApi.GetInclusionStates( - new[] {TEST_HASH}, - new[] {_iotaApi.GetNodeInfo().LatestSolidSubtangleMilestone}); - Assert.IsNotNull(res.States); - } - - [TestMethod] // very long execution - public void ShouldGetTransactionsToApprove() - { - var res = _iotaApi.GetTransactionsToApprove(27); - Assert.IsNotNull(res.TrunkTransaction); - Assert.IsNotNull(res.BranchTransaction); - } - - [TestMethod] - public void ShouldFindTransactions() - { - var test = TEST_BUNDLE; - // ReSharper disable once UnusedVariable - var resp = _iotaApi.FindTransactions(new[] {test}.ToList(), - new[] {test}.ToList(), new[] {test}.ToList(), new[] {test}.ToList()); - } - - [TestMethod] - public void ShouldGetBalances() - { - var res = _iotaApi.GetBalances(new[] {TEST_ADDRESS_WITH_CHECKSUM}.ToList(), 100); - Assert.IsNotNull(res.Balances); - Assert.IsNotNull(res.References); - Assert.IsNotNull(res.MilestoneIndex); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard.Tests/Pow/KerlTest.cs b/IotaApi.Standard.Tests/Pow/KerlTest.cs deleted file mode 100644 index f79d434..0000000 --- a/IotaApi.Standard.Tests/Pow/KerlTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Iota.Api.Standard.Pow; -using Iota.Api.Standard.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests.Pow -{ - [TestClass] - public class KerlTest - { - [TestMethod] - public void ShouldCreateValidHash1() - { - var trits = Converter.ToTrits( - "GYOMKVTSNHVJNCNFBBAH9AAMXLPLLLROQY99QN9DLSJUHDPBLCFFAIQXZA9BKMBJCYSFHFPXAHDWZFEIZ"); - var kerl = new Kerl(); - kerl.Reset(); - kerl.Absorb(trits, 0, trits.Length); - var hashTrits = new int[trits.Length]; - kerl.Squeeze(hashTrits, 0, 243); - var hash = Converter.ToTrytes(hashTrits); - Assert.AreEqual(hash, "OXJCNFHUNAHWDLKKPELTBFUCVW9KLXKOGWERKTJXQMXTKFKNWNNXYD9DMJJABSEIONOSJTTEVKVDQEWTW"); - } - - [TestMethod] - public void ShouldCreateValidHash2() - { - var trits = Converter.ToTrits( - "9MIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJFGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH"); - var kerl = new Kerl(); - kerl.Reset(); - kerl.Absorb(trits, 0, trits.Length); - var hashTrits = new int[trits.Length * 2]; - kerl.Squeeze(hashTrits, 0, 243 * 2); - var hash = Converter.ToTrytes(hashTrits); - Assert.AreEqual(hash, - "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA"); - } - - [TestMethod] - public void ShouldCreateValidHash3() - { - var trits = Converter.ToTrits( - "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA"); - var kerl = new Kerl(); - kerl.Reset(); - kerl.Absorb(trits, 0, trits.Length); - var hashTrits = new int[trits.Length]; - kerl.Squeeze(hashTrits, 0, 243 * 2); - var hash = Converter.ToTrytes(hashTrits); - Assert.AreEqual(hash, - "LUCKQVACOGBFYSPPVSSOXJEKNSQQRQKPZC9NXFSMQNRQCGGUL9OHVVKBDSKEQEBKXRNUJSRXYVHJTXBPDWQGNSCDCBAIRHAQCOWZEBSNHIJIGPZQITIBJQ9LNTDIBTCQ9EUWKHFLGFUVGGUWJONK9GBCDUIMAYMMQX"); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard.Tests/Pow/LocalPoWTest.cs b/IotaApi.Standard.Tests/Pow/LocalPoWTest.cs deleted file mode 100644 index af85382..0000000 --- a/IotaApi.Standard.Tests/Pow/LocalPoWTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Pow; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests.Pow -{ - [TestClass] - public class LocalPoWTest - { - private static readonly string TEST_SEED1 = - "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2 = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZC"; - - private static readonly string TEST_MESSAGE = "JUSTANOTHERJOTATEST"; - private static readonly string TEST_TAG = "JOTASPAM9999999999999999999"; - private static readonly int MIN_WEIGHT_MAGNITUDE = 14; - private static readonly int DEPTH = 9; - - private IotaApi _iotaClient; - - [TestInitialize] - public void Setup() - { - _iotaClient = new IotaApi("node.iotawallet.info", 14265) - { - LocalPow = new PearlDiverLocalPoW() - }; - } - - [TestMethod] - public void ShouldSendTransfer() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 0, TEST_MESSAGE, TEST_TAG) - }; - var result = _iotaClient.SendTransfer( - TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers.ToArray(), - null, null, false, false); - Assert.IsNotNull(result); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard.Tests/Pow/PearlDiverTest.cs b/IotaApi.Standard.Tests/Pow/PearlDiverTest.cs deleted file mode 100644 index 6ed6e67..0000000 --- a/IotaApi.Standard.Tests/Pow/PearlDiverTest.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Text; -using Iota.Api.Standard.Pow; -using Iota.Api.Standard.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests.Pow -{ - [TestClass] - public class PearlDiverTest - { - - private const int TryteLength = 2673; - private const int MinWeightMagnitude = 9; - private const int NumCores = -1; // use n-1 cores - - private static readonly Random Random = new Random(); - private PearlDiver _pearlDiver; - private int[] _hashTrits; - - [TestInitialize] - public void Setup() - { - _pearlDiver = new PearlDiver(); - _hashTrits = new int[Sponge.HashLength]; - } - - - [TestMethod] - public void TestRandomTryteHash() - { - string testTrytes = GetRandomTrytes(); - - string hash = GetHashFor(testTrytes); - - string subHash = hash.Substring(Sponge.HashLength / 3 - MinWeightMagnitude / 3); - - bool success = InputValidator.IsNinesTrytes(subHash,subHash.Length); - if (!success) - { - Console.WriteLine(testTrytes); - } - - Assert.IsTrue(success, "The hash should have n nines"); - } - - [TestMethod] - [Ignore] - public void TestRandomTryteHash100() - { - for (int i = 0; i < 100; i++) - { - string testTrytes = GetRandomTrytes(); - - string hash = GetHashFor(testTrytes); - - string subHash = hash.Substring(Sponge.HashLength / 3 - MinWeightMagnitude / 3); - - bool success = InputValidator.IsNinesTrytes(subHash, subHash.Length); - if (!success) - { - Console.WriteLine(testTrytes); - } - - Assert.IsTrue(success, "The hash should have n nines"); - } - } - - private string GetRandomTrytes() - { - var trytes = new StringBuilder(); - - for (int i = 0; i < TryteLength; i++) - { - trytes.Append(Constants.TryteAlphabet[Random.Next(27)]); - } - - return trytes.ToString(); - } - - private string GetHashFor(string trytes) - { - Sponge curl = new Curl(CurlMode.CurlP81); - int[] myTrits = Converter.ToTrits(trytes); - - bool result = _pearlDiver.Search(myTrits, MinWeightMagnitude, NumCores); - - Assert.IsTrue(result,"Search Failed"); - - curl.Absorb(myTrits, 0, myTrits.Length); - curl.Squeeze(_hashTrits, 0, Sponge.HashLength); - curl.Reset(); - - return Converter.ToTrytes(_hashTrits); - } - } -} diff --git a/IotaApi.Standard.Tests/Utils/BigIntConverterTest.cs b/IotaApi.Standard.Tests/Utils/BigIntConverterTest.cs deleted file mode 100644 index 6337882..0000000 --- a/IotaApi.Standard.Tests/Utils/BigIntConverterTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using Iota.Api.Standard.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Org.BouncyCastle.Math; - -namespace Iota.Api.Standard.Tests.Utils -{ - [TestClass] - public class BigIntConverterTest - { - private static readonly Random Random = new Random(); - // trits<->BigInteger<->byte - - [TestMethod] - public void TestTritsAndBigInt() - { - var inputTrits = new int[243]; - for (var i = 0; i < inputTrits.Length; i++) inputTrits[i] = Random.Next(3) - 1; - - var bigInt = BigIntConverter.BigIntFromTrits(inputTrits, 0, inputTrits.Length); - - var outputTrits = new int[inputTrits.Length]; - BigIntConverter.TritsFromBigInt(bigInt, outputTrits, 0, outputTrits.Length); - - for (var i = 0; i < inputTrits.Length; i++) Assert.AreEqual(inputTrits[i], outputTrits[i]); - } - - [TestMethod] - public void TestBigIntAndByte() - { - var bytes = new byte[48]; - var bigInt0 = new BigInteger("-123456"); - - BigIntConverter.BytesFromBigInt(bigInt0, bytes, 0, bytes.Length); - var bigInt1 = BigIntConverter.BigIntFromBytes(bytes, 0, bytes.Length); - - Assert.AreEqual(bigInt0, bigInt1); - } - - [TestMethod] - public void TestFixedBigInt() - { - var inputTrits = new int[243]; - var outputTrits = new int[243]; - var bytes = new byte[384 / 8]; - - for (var i = 0; i < inputTrits.Length; i++) inputTrits[i] = Random.Next(3) - 1; - - inputTrits[inputTrits.Length - 1] = 0; - FixedBigIntConverter.FromTritsToBytes(inputTrits, bytes); - FixedBigIntConverter.FromBytesToTrits(bytes, outputTrits); - outputTrits[outputTrits.Length - 1] = 0; - - for (var i = 0; i < inputTrits.Length; i++) Assert.AreEqual(inputTrits[i], outputTrits[i]); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard.Tests/Utils/ChecksumTest.cs b/IotaApi.Standard.Tests/Utils/ChecksumTest.cs deleted file mode 100644 index 77a5334..0000000 --- a/IotaApi.Standard.Tests/Utils/ChecksumTest.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Iota.Api.Standard.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests.Utils -{ - [TestClass] - public class ChecksumTest - { - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZC"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC"; - - [TestMethod] - public void ShouldAddChecksum() - { - Assert.AreEqual(Checksum.AddChecksum(TEST_ADDRESS_WITHOUT_CHECKSUM), TEST_ADDRESS_WITH_CHECKSUM); - } - - [TestMethod] - public void ShouldRemoveChecksum() - { - Assert.AreEqual(TEST_ADDRESS_WITH_CHECKSUM.RemoveChecksum(), TEST_ADDRESS_WITHOUT_CHECKSUM); - } - - [TestMethod] - public void ShouldIsValidChecksum() - { - Assert.AreEqual(TEST_ADDRESS_WITH_CHECKSUM.IsValidChecksum(), true); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard.Tests/Utils/InputValidatorTests.cs b/IotaApi.Standard.Tests/Utils/InputValidatorTests.cs deleted file mode 100644 index 45ffbe4..0000000 --- a/IotaApi.Standard.Tests/Utils/InputValidatorTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Collections.Generic; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests.Utils -{ - [TestClass] - public class InputValidatorTests - { - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM = - "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM = - "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA"; - - private static readonly string TEST_TRYTES = - "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"; - - private static readonly string TEST_HASH = - "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999"; - - private static readonly string TEST_MESSAGE = "JOTA"; - private static readonly string TEST_TAG = "JOTASPAM9999999999999999999"; - - [TestMethod] - public void ShouldIsAddress() - { - Assert.AreEqual(InputValidator.IsAddress(TEST_ADDRESS_WITHOUT_CHECKSUM), true); - } - - [TestMethod] - public void ShouldCheckAddress() - { - Assert.AreEqual(InputValidator.IsAddress(TEST_ADDRESS_WITHOUT_CHECKSUM), true); - } - - [TestMethod] - public void ShouldIsTrytes() - { - Assert.AreEqual(InputValidator.IsTrytes(TEST_TRYTES, TEST_TRYTES.Length), true); - } - - [TestMethod] - public void ShouldIsValue() - { - Assert.AreEqual(InputValidator.IsValue("1234"), true); - } - - [TestMethod] - public void IsValueNeg() - { - Assert.AreEqual(InputValidator.IsValue("-1234"), true); - } - - [TestMethod] - public void IsValueNeg2() - { - Assert.AreEqual(InputValidator.IsValue("-"), false); - } - - [TestMethod] - public void ShouldIsArrayOfHashes() - { - Assert.AreEqual(InputValidator.IsArrayOfHashes(new[] {TEST_HASH, TEST_HASH}), true); - } - - [TestMethod] - public void ShouldIsArrayOfTrytes() - { - Assert.AreEqual(InputValidator.IsArrayOfTrytes(new[] {TEST_TRYTES, TEST_TRYTES}, 2673), true); - } - - [TestMethod] - public void ShouldIsNinesTrytes() - { - Assert.AreEqual(InputValidator.IsNinesTrytes("999999999", 9), true); - } - - - [TestMethod] - public void ShouldIsTransfersCollectionCorrect() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG), - new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG) - }; - Assert.AreEqual(InputValidator.IsTransfersCollectionValid(transfers), true); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard.Tests/Utils/MultisigTest.cs b/IotaApi.Standard.Tests/Utils/MultisigTest.cs deleted file mode 100644 index 51ec308..0000000 --- a/IotaApi.Standard.Tests/Utils/MultisigTest.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Pow; -using Iota.Api.Standard.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests.Utils -{ - [TestClass] - public class MultisigTest - { - private const string TestSeed1 = "ABCDFG"; - private const string TestSeed2 = "FDSAG"; - - private const string RemainderAddress = - "NZRALDYNVGJWUVLKDWFKJVNYLWQGCWYCURJIIZRLJIKSAIVZSGEYKTZRDBGJLOA9AWYJQB9IPWRAKUC9FBDRZJZXZG"; - - private const string ReceiveAddress = - "ZGHXPZYDKXPEOSQTAQOIXEEI9K9YKFKCWKYYTYAUWXK9QZAVMJXWAIZABOXHHNNBJIEBEUQRTBWGLYMTX"; - - private const string TestTag = "JOTASPAM9999999999999999999"; - - - private IotaApi _iotaClient; - - [TestInitialize] - public void CreateApiClientInstance() - { - _iotaClient = new IotaApi("node.iotawallet.info", 14265); - } - - [TestMethod] - public void BasicMultiSigTest() - { - Multisig ms = new Multisig(); - - // First co-signer uses security level 3 and index 0 for the private key - string digestOne = ms.GetDigest(TestSeed1, 3, 0); - - // We initiate the multisig address generation by absorbing the key digest - ms.AddAddressDigest(new[] {digestOne}); - - // Second cosigner also uses security level 3 and index 0 for the private key - string digestTwo = ms.GetDigest(TestSeed2, 3, 0); - - // Add the multisig by absorbing the second cosigners key digest - ms.AddAddressDigest(new[] {digestTwo}); - - // finally we generate the multisig address itself - string multiSigAddress = ms.FinalizeAddress(); - - Console.WriteLine("MultisigAddress = " + multiSigAddress); - - - bool isValidMultisigAddress = ms.ValidateAddress(multiSigAddress, - new[] {Converter.ToTrits(digestOne), Converter.ToTrits(digestTwo)}); - - Console.WriteLine("Is a valid multisig address " + isValidMultisigAddress); - - Assert.IsTrue(isValidMultisigAddress, "Address is not a valid multisigAddress"); - - List transfers = new List - { - new Transfer(ReceiveAddress, 999, "", TestTag) - }; - - List trxs = - _iotaClient.InitiateTransfer(6, multiSigAddress, RemainderAddress, transfers, true); - - Bundle bundle = new Bundle(trxs, trxs.Count); - - bundle = ms.AddSignature(bundle, multiSigAddress, ms.GetKey(TestSeed1, 0, 3)); - - bundle = ms.AddSignature(bundle, multiSigAddress, ms.GetKey(TestSeed2, 0, 3)); - - - Signing sgn = new Signing(new Kerl()); - - bool isValidSignature = sgn.ValidateSignatures(bundle, multiSigAddress); - Console.WriteLine("Result of multi-signature validation is " + isValidSignature); - Assert.IsTrue(isValidSignature, "MultiSignature not valid"); - - } - } -} diff --git a/IotaApi.Standard.Tests/Utils/TrytesConverterTest.cs b/IotaApi.Standard.Tests/Utils/TrytesConverterTest.cs deleted file mode 100644 index 7f40866..0000000 --- a/IotaApi.Standard.Tests/Utils/TrytesConverterTest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using Iota.Api.Standard.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Api.Standard.Tests.Utils -{ - [TestClass] - public class TrytesConverterTest - { - private static readonly Random Random = new Random(); - - [TestMethod] - public void ShouldConvertStringToTrytes() - { - Assert.AreEqual("IC", TrytesConverter.ToTrytes("Z")); - Assert.AreEqual(TrytesConverter.ToTrytes("JOTA JOTA"), "TBYBCCKBEATBYBCCKB"); - } - - [TestMethod] - public void ShouldConvertTrytesToString() - { - Assert.AreEqual("Z", TrytesConverter.ToString("IC")); - Assert.AreEqual(TrytesConverter.ToString("TBYBCCKBEATBYBCCKB"), "JOTA JOTA"); - } - - [TestMethod] - public void ShouldConvertBackAndForth() - { - var str = RandomString(1000); - var back = TrytesConverter.ToString(TrytesConverter.ToTrytes(str)); - Assert.AreEqual(str, back); - } - - public static string RandomString(int length) - { - const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[Random.Next(s.Length)]).ToArray()); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/AddNeighborsRequest.cs b/IotaApi.Standard/Core/AddNeighborsRequest.cs deleted file mode 100644 index c9b108c..0000000 --- a/IotaApi.Standard/Core/AddNeighborsRequest.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - - /// - /// This class represents the core API request 'AddNeighbors'. - /// It is used to add a neighbor to the node - /// - /// - public class AddNeighborsRequest : IotaRequest - { - /// - /// Gets or sets the uris. - /// - /// - /// The uris. - /// - public List Uris { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The uris of the neighbors to add. - public AddNeighborsRequest(List uris) : base(Core.Command.AddNeighbors.GetCommandString()) - { - Uris = uris; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Uris)}: {string.Join(",", Uris)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/AttachToTangleRequest.cs b/IotaApi.Standard/Core/AttachToTangleRequest.cs deleted file mode 100644 index 4ad947b..0000000 --- a/IotaApi.Standard/Core/AttachToTangleRequest.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - - /// - /// This class represents the core API request 'AttachToTangle'. - /// It is used to attach trytes to the tangle. - /// - public class AttachToTangleRequest : IotaRequest - { - private const int MinWeightMagnitudeMin = 18; - private int _minWeightMagnitude = MinWeightMagnitudeMin; - - /// - /// Initializes a new instance of the class. - /// - /// The trunk transaction. - /// The branch transaction. - /// The trytes. - /// The minimum weight magnitude. - public AttachToTangleRequest(string trunkTransaction, string branchTransaction, string[] trytes, - int minWeightMagnitude = 18) : base(Core.Command.AttachToTangle.GetCommandString()) - { - TrunkTransaction = trunkTransaction; - BranchTransaction = branchTransaction; - Trytes = trytes; - MinWeightMagnitude = minWeightMagnitude; - - if (Trytes == null) - Trytes = new string[0]; - } - - /// - /// Proof of Work intensity. Minimum value is 18 - /// - public int MinWeightMagnitude - { - get { return _minWeightMagnitude; } - set - { - if (value > MinWeightMagnitudeMin) - _minWeightMagnitude = value; - } - } - - /// - /// Trunk transaction to approve. - /// - public string TrunkTransaction { get; set; } - - /// - /// Branch transaction to approve. - /// - public string BranchTransaction { get; set; } - - /// - /// List of trytes (raw transaction data) to attach to the tangle. - /// - public string[] Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(MinWeightMagnitude)}: {MinWeightMagnitude}, {nameof(TrunkTransaction)}: {TrunkTransaction}, {nameof(BranchTransaction)}: {BranchTransaction}, {nameof(Trytes)}: {Trytes}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/AttachToTangleResponse.cs b/IotaApi.Standard/Core/AttachToTangleResponse.cs deleted file mode 100644 index 75e2736..0000000 --- a/IotaApi.Standard/Core/AttachToTangleResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// Response of - /// - public class AttachToTangleResponse - { - /// - /// Gets or sets the trytes. - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/BroadcastTransactionsRequest.cs b/IotaApi.Standard/Core/BroadcastTransactionsRequest.cs deleted file mode 100644 index 329d05f..0000000 --- a/IotaApi.Standard/Core/BroadcastTransactionsRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// Broadcast a list of transactions to all neighbors. The input trytes for this call are provided by attachToTangle - /// - public class BroadcastTransactionsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The trytes. - public BroadcastTransactionsRequest(List trytes) - : base(Core.Command.BroadcastTransactions.GetCommandString()) - { - Trytes = trytes; - } - - /// - /// Gets or sets the trytes representing the transactions - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/BroadcastTransactionsResponse.cs b/IotaApi.Standard/Core/BroadcastTransactionsResponse.cs deleted file mode 100644 index f8feb07..0000000 --- a/IotaApi.Standard/Core/BroadcastTransactionsResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// Response of - /// - public class BroadcastTransactionsResponse - { - // empty - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/Command.cs b/IotaApi.Standard/Core/Command.cs deleted file mode 100644 index b20be21..0000000 --- a/IotaApi.Standard/Core/Command.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.ComponentModel; - -namespace Iota.Api.Standard.Core -{ - /// - /// This enumeration defines the core API call commands - /// - public enum Command - { - /// - /// Adds neighbours to the node - /// - [Description("addNeighbors")] AddNeighbors, - - /// - /// Attaches to the tangle - /// - [Description("attachToTangle")] AttachToTangle, - - /// - /// Broadcasts transactions - /// - [Description("broadcastTransactions")] BroadcastTransactions, - - /// - /// Finds the transactions using different search criteria - /// - [Description("findTransactions")] FindTransactions, - - /// - /// Gets the balances - /// - [Description("getBalances")] GetBalances, - - /// - /// Gets the inclusion state - /// - [Description("getInclusionStates")] GetInclusionStates, - - /// - /// Gets the neighbours of the node - /// - [Description("getNeighbors")] GetNeighbors, - - /// - /// Get information about the node. - /// - [Description("getNodeInfo")] GetNodeInfo, - - /// - /// Gets the tips of the node - /// - [Description("getTips")] GetTips, - - /// - /// Gets the transactions to approve - /// - [Description("getTransactionsToApprove")] GetTransactionsToApprove, - - /// - /// Gets the trytes - /// - [Description("getTrytes")] GetTrytes, - - /// - /// Interrupt attaching to the tangle - /// - [Description("interruptAttachingToTangle")] InterruptAttachingToTangle, - - /// - /// Removes neighbours from the node - /// - [Description("removeNeighbors")] RemoveNeighbors, - - /// - /// Stores transactions - /// - [Description("storeTransactions")] StoreTransactions, - - /// - /// Get Missing Transactions - /// - [Description("getMissingTransactions")] GetMissingTransactions, - - /// - /// Check Consistency - /// - [Description("checkConsistency")] CheckConsistency, - - /// - /// Were Addresses SpentFrom - /// - [Description("wereAddressesSpentFrom")] WereAddressesSpentFrom, - - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/EnumHelper.cs b/IotaApi.Standard/Core/EnumHelper.cs deleted file mode 100644 index 0a29e1c..0000000 --- a/IotaApi.Standard/Core/EnumHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.ComponentModel; -using System.Reflection; - -namespace Iota.Api.Standard.Core -{ - /// - /// Helper class that extracts the command string corresponding to the different s - /// - public static class EnumHelper - { - /// - /// Retrieve the description on the enum - /// - /// The Enumeration - /// A string representing the friendly name - public static string GetCommandString(this Enum en) - { - Type type = en.GetType(); - - MemberInfo[] memInfo = type.GetMember(en.ToString()); - - if (memInfo != null && memInfo.Length > 0) - { - object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); - - if (attrs != null && attrs.Length > 0) - { - return ((DescriptionAttribute) attrs[0]).Description; - } - } - - return en.ToString(); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/ErrorResponse.cs b/IotaApi.Standard/Core/ErrorResponse.cs deleted file mode 100644 index 47eb381..0000000 --- a/IotaApi.Standard/Core/ErrorResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - internal class ErrorResponse - { - public string Error { get; set; } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/FindTransactionsRequest.cs b/IotaApi.Standard/Core/FindTransactionsRequest.cs deleted file mode 100644 index 72eefc9..0000000 --- a/IotaApi.Standard/Core/FindTransactionsRequest.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core api request 'FindTransactions' - /// - public class FindTransactionsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The bundles. - /// The addresses. - /// The tags. - /// The approvees. - public FindTransactionsRequest(List bundles, List addresses, List tags, - List approvees) : base(Core.Command.FindTransactions.GetCommandString()) - { - Bundles = bundles; - Addresses = addresses; - Tags = tags; - Approvees = approvees; - - if (Bundles == null) - Bundles = new List(); - if (Addresses == null) - Addresses = new List(); - if (Tags == null) - Tags = new List(); - if (Approvees == null) - Approvees = new List(); - } - - /// - /// Gets or sets the bundles. - /// - /// - /// The bundles. - /// - public List Bundles { get; set; } - - /// - /// Gets or sets the addresses. - /// - /// - /// The addresses. - /// - public List Addresses { get; set; } - - /// - /// Gets or sets the tags. - /// - /// - /// The tags. - /// - public List Tags { get; set; } - - /// - /// Gets or sets the approvees. - /// - /// - /// The approvees. - /// - public List Approvees { get; set; } - - /// - /// - /// - /// - public bool ShouldSerializeBundles() - { - return Bundles.Count > 0; - } - - /// - /// - /// - /// - public bool ShouldSerializeAddresses() - { - return Addresses.Count > 0; - } - - /// - /// - /// - /// - public bool ShouldSerializeTags() - { - return Tags.Count > 0; - } - - /// - /// - /// - /// - public bool ShouldSerializeApprovees() - { - return Approvees.Count > 0; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/FindTransactionsResponse.cs b/IotaApi.Standard/Core/FindTransactionsResponse.cs deleted file mode 100644 index 7eca193..0000000 --- a/IotaApi.Standard/Core/FindTransactionsResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// Response of - /// - public class FindTransactionsResponse - { - /// - /// Gets or sets the hashes. - /// - /// - /// The hashes. - /// - public List Hashes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Hashes)}: {string.Join(",",Hashes)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GenericIotaCoreApi.cs b/IotaApi.Standard/Core/GenericIotaCoreApi.cs deleted file mode 100644 index 3e94785..0000000 --- a/IotaApi.Standard/Core/GenericIotaCoreApi.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using Iota.Api.Standard.Utils.Rest; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents a generic version of the core API that is used internally - /// - /// - public class GenericIotaCoreApi : IGenericIotaCoreApi - { - private readonly string _host; - private readonly int _port; - - /// - /// Initializes a new instance of the class. - /// - /// The host. - /// The port. - public GenericIotaCoreApi(string host, int port) - { - _host = host; - _port = port; - } - - /// - /// Gets the hostname. - /// - /// - /// The hostname. - /// - public string Hostname => _host; - - /// - /// Gets the port. - /// - /// - /// The port. - /// - public int Port => _port; - - /// - /// Requests the specified request. - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// - public TResponse Request(TRequest request) where TResponse : new() - { - JsonWebClient jsonWebClient = new JsonWebClient(); - return jsonWebClient.GetPOSTResponseSync(new Uri(CreateBaseUrl()), - new JsonSerializer().Serialize(request)); - } - - /// - /// Requests the specified request asynchronously - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// The response action. - public void RequestAsync(TRequest request, Action responseAction) - where TResponse : new() - { - JsonWebClient jsonWebClient = new JsonWebClient(); - jsonWebClient.GetPOSTResponseAsync(new Uri(CreateBaseUrl()), - new JsonSerializer().Serialize(request), responseAction); - } - - private string CreateBaseUrl() - { - return "http://" + _host + ":" + _port; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetBalancesRequest.cs b/IotaApi.Standard/Core/GetBalancesRequest.cs deleted file mode 100644 index b08eca2..0000000 --- a/IotaApi.Standard/Core/GetBalancesRequest.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core api request 'GetBalances' - /// - public class GetBalancesRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The addresses. - /// The threshold. - public GetBalancesRequest(List addresses, long threshold = 100) - : base(Core.Command.GetBalances.GetCommandString()) - { - Addresses = addresses; - Threshold = threshold; - } - - /// - /// Gets the threshold. - /// - /// - /// The threshold. - /// - public long Threshold { get; } - - /// - /// Gets the addresses. - /// - /// - /// The addresses. - /// - public List Addresses { get; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Threshold)}: {Threshold}, {nameof(Addresses)}: {string.Join(",",Addresses)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetBalancesResponse.cs b/IotaApi.Standard/Core/GetBalancesResponse.cs deleted file mode 100644 index 8342755..0000000 --- a/IotaApi.Standard/Core/GetBalancesResponse.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// Response of - /// - public class GetBalancesResponse : IotaResponse - { - /// - /// Gets or sets the balances. - /// - /// - /// The balances. - /// - public List Balances { get; set; } - - /// - /// Gets or sets the references. - /// - /// - /// The references. - /// - public List References { get; set; } - - /// - /// Gets or sets the index of the milestone. - /// - /// - /// The index of the milestone. - /// - public int MilestoneIndex { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return - $"{nameof(Balances)}: {string.Join(",", Balances)}, {nameof(References)}: {string.Join(",", References)}, {nameof(MilestoneIndex)}: {MilestoneIndex}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetInclusionStatesRequest.cs b/IotaApi.Standard/Core/GetInclusionStatesRequest.cs deleted file mode 100644 index fbb71a2..0000000 --- a/IotaApi.Standard/Core/GetInclusionStatesRequest.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core API request 'GetInclusionStates' - /// - /// - public class GetInclusionStatesRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The transactions. - /// The tips. - public GetInclusionStatesRequest(string[] transactions, string[] tips) - : base(Core.Command.GetInclusionStates.GetCommandString()) - { - Transactions = transactions; - Tips = tips; - } - - /// - /// Gets the transactions. - /// - /// - /// The transactions. - /// - public string[] Transactions { get; } - - /// - /// Gets the tips. - /// - /// - /// The tips. - /// - public string[] Tips { get; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Transactions)}: {Transactions}, {nameof(Tips)}: {Tips}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetInclusionStatesResponse.cs b/IotaApi.Standard/Core/GetInclusionStatesResponse.cs deleted file mode 100644 index 2944d90..0000000 --- a/IotaApi.Standard/Core/GetInclusionStatesResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the response of - /// - /// - public class GetInclusionStatesResponse : IotaResponse - { - /// - /// Gets or sets the states. - /// - /// - /// The states. - /// - public List States { get; set; } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetNeighborsRequest.cs b/IotaApi.Standard/Core/GetNeighborsRequest.cs deleted file mode 100644 index 21b64e6..0000000 --- a/IotaApi.Standard/Core/GetNeighborsRequest.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core API request 'GetNeighbors' - /// - /// - public class GetNeighborsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public GetNeighborsRequest() : base(Core.Command.GetNeighbors.GetCommandString()) - { - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetNeighborsResponse.cs b/IotaApi.Standard/Core/GetNeighborsResponse.cs deleted file mode 100644 index d667b23..0000000 --- a/IotaApi.Standard/Core/GetNeighborsResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using Iota.Api.Standard.Model; - -namespace Iota.Api.Standard.Core -{ - /// - /// Response of - /// - public class GetNeighborsResponse - { - /// - /// Gets or sets the neighbors. - /// - /// - /// The neighbors. - /// - public List Neighbors { get; set; } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetNodeInfoRequest.cs b/IotaApi.Standard/Core/GetNodeInfoRequest.cs deleted file mode 100644 index 658ab6b..0000000 --- a/IotaApi.Standard/Core/GetNodeInfoRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// Returns information about your node - /// - public class GetNodeInfoRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public GetNodeInfoRequest() : base(Core.Command.GetNodeInfo.GetCommandString()) - { - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetTipsResponse.cs b/IotaApi.Standard/Core/GetTipsResponse.cs deleted file mode 100644 index b9ca3b6..0000000 --- a/IotaApi.Standard/Core/GetTipsResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the response of - /// - public class GetTipsResponse : IotaResponse - { - /// - /// Gets or sets the hashes. - /// - /// - /// The hashes. - /// - public List Hashes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Hashes)}: {string.Join(",", Hashes)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetTransactionsToApproveRequest.cs b/IotaApi.Standard/Core/GetTransactionsToApproveRequest.cs deleted file mode 100644 index 200987d..0000000 --- a/IotaApi.Standard/Core/GetTransactionsToApproveRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core API call 'GetTransactionsToApprove' - /// - public class GetTransactionsToApproveRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The depth. - public GetTransactionsToApproveRequest(int depth) - : base(Core.Command.GetTransactionsToApprove.GetCommandString()) - { - Depth = depth; - } - - /// - /// Gets the depth. - /// - /// - /// The depth. - /// - public int Depth { get; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Depth)}: {Depth}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetTransactionsToApproveResponse.cs b/IotaApi.Standard/Core/GetTransactionsToApproveResponse.cs deleted file mode 100644 index d093d78..0000000 --- a/IotaApi.Standard/Core/GetTransactionsToApproveResponse.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the response of - /// - public class GetTransactionsToApproveResponse : IotaResponse - { - /// - /// Gets or sets the trunk transaction. - /// - /// - /// The trunk transaction. - /// - public string TrunkTransaction { get; set; } - - /// - /// Gets or sets the branch transaction. - /// - /// - /// The branch transaction. - /// - public string BranchTransaction { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(TrunkTransaction)}: {TrunkTransaction}, {nameof(BranchTransaction)}: {BranchTransaction}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetTrytesRequest.cs b/IotaApi.Standard/Core/GetTrytesRequest.cs deleted file mode 100644 index 3d2c1b9..0000000 --- a/IotaApi.Standard/Core/GetTrytesRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core API request 'GetTrytes' - /// - public class GetTrytesRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public GetTrytesRequest() : base(Core.Command.GetTrytes.GetCommandString()) - { - - } - - /// - /// Gets or sets the hashes. - /// - /// - /// The hashes. - /// - public string[] Hashes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Hashes)}: {string.Join(",", Hashes)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/GetTrytesResponse.cs b/IotaApi.Standard/Core/GetTrytesResponse.cs deleted file mode 100644 index 41ac755..0000000 --- a/IotaApi.Standard/Core/GetTrytesResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the response of - /// - public class GetTrytesResponse - { - - /// - /// Gets or sets the trytes. - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/IGenericIotaCoreApi.cs b/IotaApi.Standard/Core/IGenericIotaCoreApi.cs deleted file mode 100644 index f2018e9..0000000 --- a/IotaApi.Standard/Core/IGenericIotaCoreApi.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; - -namespace Iota.Api.Standard.Core -{ - /// - /// This interface abstracts a generic version of the core api that is used internally. - /// - public interface IGenericIotaCoreApi - { - /// - /// Gets the hostname. - /// - /// - /// The hostname. - /// - string Hostname { get; } - - /// - /// Gets the port. - /// - /// - /// The port. - /// - int Port { get; } - - /// - /// Requests the specified request. - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// - TResponse Request(TRequest request) where TResponse : new(); - - /// - /// Requests the specified request asynchronously - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// The response action. - void RequestAsync(TRequest request, Action responseAction) - where TResponse : new(); - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/ILocalPoW.cs b/IotaApi.Standard/Core/ILocalPoW.cs deleted file mode 100644 index 1588610..0000000 --- a/IotaApi.Standard/Core/ILocalPoW.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// - /// - public interface ILocalPoW - { - /// - /// - /// - /// - /// - /// - string PerformPoW(string trytes, int minWeightMagnitude); - } -} diff --git a/IotaApi.Standard/Core/InterruptAttachingToTangleRequest.cs b/IotaApi.Standard/Core/InterruptAttachingToTangleRequest.cs deleted file mode 100644 index be764fe..0000000 --- a/IotaApi.Standard/Core/InterruptAttachingToTangleRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core api request 'InterruptAttachingToTangle' - /// - public class InterruptAttachingToTangleRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public InterruptAttachingToTangleRequest() : base(Core.Command.InterruptAttachingToTangle.GetCommandString()) - { - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/InterruptAttachingToTangleResponse.cs b/IotaApi.Standard/Core/InterruptAttachingToTangleResponse.cs deleted file mode 100644 index 0909d06..0000000 --- a/IotaApi.Standard/Core/InterruptAttachingToTangleResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the response of - /// - /// - public class InterruptAttachingToTangleResponse : IotaResponse - { - - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/IotaCoreApi.cs b/IotaApi.Standard/Core/IotaCoreApi.cs deleted file mode 100644 index ec30250..0000000 --- a/IotaApi.Standard/Core/IotaCoreApi.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Utils; -using RestSharp.Extensions; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class provides access to the Iota core API - /// - public class IotaCoreApi - { - private readonly IGenericIotaCoreApi _genericIotaCoreApi; - - /// - /// - /// - public ILocalPoW LocalPow { get; set; } - - /// - /// Creates a core api object that uses the specified connection settings to connect to a node - /// - /// hostname or API address of a node to interact with - /// tcp/udp port - public IotaCoreApi(string host, int port) - { - _genericIotaCoreApi = new GenericIotaCoreApi(host, port); - } - - /// - /// Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work. - /// You need to supply branchTransaction as well as trunkTransaction - /// (basically the tips which you're going to validate and reference with this transaction) - /// - both of which you'll get through the getTransactionsToApprove API call. - /// - /// Trunk transaction to approve. - /// Branch transaction to approve. - /// List of trytes (raw transaction data) to attach to the tangle. - /// Proof of Work intensity. Minimum value is 18 - /// The returned value contains a different set of tryte values which you can input into broadcastTransactions and storeTransactions. - /// The returned tryte value, the last 243 trytes basically consist of the: trunkTransaction + branchTransaction + nonce. - /// These are valid trytes which are then accepted by the network. - public AttachToTangleResponse AttachToTangle(string trunkTransaction, string branchTransaction, - string[] trytes, int minWeightMagnitude = 18) - { - if (!InputValidator.IsHash(trunkTransaction)) - throw new ArgumentException("Invalid hashes provided."); - - if (!InputValidator.IsHash(branchTransaction)) - throw new ArgumentException("Invalid hashes provided."); - - if (!InputValidator.IsArrayOfTrytes(trytes, 2673)) - throw new ArgumentException("Invalid trytes provided."); - - if (LocalPow != null) - { - var response = new AttachToTangleResponse - { - Trytes = new List() - }; - - string previousTransaction = null; - foreach (var t in trytes) - { - var txn = new Transaction(t) - { - TrunkTransaction = previousTransaction ?? trunkTransaction, - BranchTransaction = previousTransaction == null ? branchTransaction : trunkTransaction - }; - - if (string.IsNullOrEmpty(txn.Tag) || txn.Tag.Matches("9*")) - txn.Tag = txn.ObsoleteTag; - txn.AttachmentTimestamp = IotaApiUtils.CreateTimeStampNow(); - txn.AttachmentTimestampLowerBound = 0; - txn.AttachmentTimestampUpperBound = 3_812_798_742_493L; - - var resultTrytes = LocalPow.PerformPoW(txn.ToTransactionTrytes(), minWeightMagnitude); - - previousTransaction = new Transaction(resultTrytes).Hash; - - response.Trytes.Add(resultTrytes); - } - - return response; - } - - AttachToTangleRequest attachToTangleRequest = new AttachToTangleRequest(trunkTransaction, branchTransaction, - trytes, minWeightMagnitude); - return _genericIotaCoreApi.Request(attachToTangleRequest); - } - - /// - /// Broadcasts the transactions. - /// - /// The transactions in trytes representation - /// the BroadcastTransactionsResponse - public BroadcastTransactionsResponse BroadcastTransactions(List trytes) - { - return - _genericIotaCoreApi.Request( - new BroadcastTransactionsRequest(trytes)); - } - - /// - /// Finds the transactions using the specified arguments as search criteria - /// - /// The addresses. - /// The tags. - /// The approves. - /// The bundles. - /// a FindTransactionsResponse, see - public FindTransactionsResponse FindTransactions(List addresses, List tags, - List approves, List bundles) - { - var findTransactionsRequest = new FindTransactionsRequest(bundles, addresses, tags, approves); - return - _genericIotaCoreApi.Request(findTransactionsRequest); - } - - /// - /// Gets the balances. - /// - /// The addresses. - /// The threshold. - /// It returns the confirmed balance which a list of addresses have at the latest confirmed milestone. - /// In addition to the balances, it also returns the milestone as well as the index with which the confirmed balance was determined. - /// The balances is returned as a list in the same order as the addresses were provided as input. - public GetBalancesResponse GetBalances(List addresses, long threshold) - { - List addressesWithoutChecksum = new List(); - foreach (var address in addresses) - { - string address0 = address.RemoveChecksum(); - addressesWithoutChecksum.Add(address0); - } - - GetBalancesRequest getBalancesRequest = new GetBalancesRequest(addressesWithoutChecksum, threshold); - return _genericIotaCoreApi.Request(getBalancesRequest); - } - - /// - /// Gets the inclusion states of the specified transactions - /// - /// The transactions. - /// The milestones. - /// a GetInclusionStatesResponse, see - public GetInclusionStatesResponse GetInclusionStates(string[] transactions, string[] milestones) - { - return - _genericIotaCoreApi.Request( - new GetInclusionStatesRequest(transactions, milestones)); - } - - /// - /// Stores the specified transactions in trytes into the local storage. The trytes to be used for this call are returned by attachToTangle. - /// - /// The trytes representing the transactions - /// a - public StoreTransactionsResponse StoreTransactions(List trytes) - { - return - _genericIotaCoreApi.Request( - new StoreTransactionsRequest(trytes)); - } - - /// - /// Gets the node information. - /// - /// a containing information about the node. - public GetNodeInfoResponse GetNodeInfo() - { - return _genericIotaCoreApi.Request(new GetNodeInfoRequest()); - } - - /// - /// Gets the tips. - /// - /// a containing a list of tips - public GetTipsResponse GetTips() - { - GetTipsRequest getTipsRequest = new GetTipsRequest(); - return _genericIotaCoreApi.Request(getTipsRequest); - } - - /// - /// Gets the transactions to approve. - /// - /// The depth is the number of bundles to go back to determine the transactions for approval. - /// The higher your depth value, the more "babysitting" you do for the network (as you have to confirm more transactions). - /// trunkTransaction and branchTransaction (result of the Tip selection) - public GetTransactionsToApproveResponse GetTransactionsToApprove(int depth) - { - GetTransactionsToApproveRequest getTransactionsToApproveRequest = new GetTransactionsToApproveRequest(depth); - return - _genericIotaCoreApi.Request( - getTransactionsToApproveRequest); - } - - /// - /// Gets the raw transaction data (trytes) of a specific transaction. - /// These trytes can then be easily converted into the actual transaction object using the constructor of Transaction - /// - /// The hashes of the transactions - /// a containing a list of trytes - public GetTrytesResponse GetTrytes(params string[] hashes) - { - GetTrytesRequest getTrytesRequest = new GetTrytesRequest() {Hashes = hashes}; - return _genericIotaCoreApi.Request(getTrytesRequest); - } - - /// - /// Interrupts and completely aborts the attachToTangle process. - /// - /// an - public InterruptAttachingToTangleResponse InterruptAttachingToTangle() - { - InterruptAttachingToTangleRequest request = new InterruptAttachingToTangleRequest(); - return - _genericIotaCoreApi.Request( - request); - } - - /// - /// Gets the neighbors the node is connected to - /// - /// A containing the set of neighbors the node is connected to as well as their activity count. The activity counter is reset after restarting IRI. - public GetNeighborsResponse GetNeighbors() - { - GetNeighborsRequest getNeighborsRequest = new GetNeighborsRequest(); - return _genericIotaCoreApi.Request(getNeighborsRequest); - } - - /// - /// Adds the neighbor(s) to the node. It should be noted that this is only temporary, and the added neighbors will be removed from your set of neighbors after you relaunch IRI. - /// - /// The uris of the neighbors to add. The URI (Unique Resource Identification) format is "udp://IPADDRESS:PORT" - /// containing the number of added Neighbors - public AddNeighborsResponse AddNeighbors(params string[] uris) - { - return - _genericIotaCoreApi.Request( - new AddNeighborsRequest(uris.ToList())); - } - - /// - /// Removes the neighbor(s) from the node. - /// - /// The uris of the neighbors to add. The URI (Unique Resource Identification) format is "udp://IPADDRESS:PORT" - /// A containing the number of removed neighbors - public RemoveNeighborsResponse RemoveNeighbors(params string[] uris) - { - RemoveNeighborsRequest removeNeighborsRequest = new RemoveNeighborsRequest(uris.ToList()); - return _genericIotaCoreApi.Request(removeNeighborsRequest); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/IotaRequest.cs b/IotaApi.Standard/Core/IotaRequest.cs deleted file mode 100644 index 5cd37d5..0000000 --- a/IotaApi.Standard/Core/IotaRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class serves as base class for the different core API calls/requests - /// - public class IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The command. - public IotaRequest(string command) - { - Command = command; - } - - /// - /// Gets or sets the command. - /// - /// - /// The command. - /// - public string Command { get; set; } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/IotaResponse.cs b/IotaApi.Standard/Core/IotaResponse.cs deleted file mode 100644 index c14feb3..0000000 --- a/IotaApi.Standard/Core/IotaResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the base class of different core API response classes - /// - public class IotaResponse - { - /// - /// Gets or sets the duration. - /// - /// - /// The duration. - /// - public long Duration { get; set; } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/RemoveNeighborsRequest.cs b/IotaApi.Standard/Core/RemoveNeighborsRequest.cs deleted file mode 100644 index 610249f..0000000 --- a/IotaApi.Standard/Core/RemoveNeighborsRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core api request 'RemoveNeighbors' - /// - /// - public class RemoveNeighborsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The uris. - public RemoveNeighborsRequest(List uris) : base(Core.Command.RemoveNeighbors.GetCommandString()) - { - Uris = uris; - } - - /// - /// Gets or sets the uris of the neighbours to remove - /// - /// - /// The uris of the neighbours to remove. - /// - public List Uris { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Uris)}: {string.Join(",", Uris)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Core/StoreTransactionsRequest.cs b/IotaApi.Standard/Core/StoreTransactionsRequest.cs deleted file mode 100644 index 5cb8f58..0000000 --- a/IotaApi.Standard/Core/StoreTransactionsRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the core API request 'StoreTransactions'. - /// It stores transactions into the local storage. The trytes to be used for this call are returned by attachToTangle. - /// - public class StoreTransactionsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The trytes. - public StoreTransactionsRequest(List trytes) : base(Core.Command.StoreTransactions.GetCommandString()) - { - Trytes = trytes; - } - - /// - /// Gets or sets the trytes. - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {Trytes}"; - } - } -} diff --git a/IotaApi.Standard/Core/StoreTransactionsResponse.cs b/IotaApi.Standard/Core/StoreTransactionsResponse.cs deleted file mode 100644 index cea9985..0000000 --- a/IotaApi.Standard/Core/StoreTransactionsResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Iota.Api.Standard.Core -{ - /// - /// This class represents the response of - /// - public class StoreTransactionsResponse - { - } -} diff --git a/IotaApi.Standard/Exception/IllegalAccessError.cs b/IotaApi.Standard/Exception/IllegalAccessError.cs deleted file mode 100644 index 8d8d6c9..0000000 --- a/IotaApi.Standard/Exception/IllegalAccessError.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception occurs when certain core API calls on the node are disabled - /// - /// - public class IllegalAccessError : System.Exception - { - - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/InvalidAddressException.cs b/IotaApi.Standard/Exception/InvalidAddressException.cs deleted file mode 100644 index b95ec9e..0000000 --- a/IotaApi.Standard/Exception/InvalidAddressException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception occurs when an invalid address is provided - /// - /// - public class InvalidAddressException : ArgumentException - { - /// - /// Initializes a new instance of the class. - /// - /// The address. - public InvalidAddressException(string address) : base("The specified address '" + address + "' is invalid") - { - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/InvalidBundleException.cs b/IotaApi.Standard/Exception/InvalidBundleException.cs deleted file mode 100644 index 5b62c24..0000000 --- a/IotaApi.Standard/Exception/InvalidBundleException.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This excpetions occurs if an invalid bundle was found or provided - /// - /// - public class InvalidBundleException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public InvalidBundleException(string message) : base(message) - { - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/InvalidSignatureException.cs b/IotaApi.Standard/Exception/InvalidSignatureException.cs deleted file mode 100644 index 54cd8d4..0000000 --- a/IotaApi.Standard/Exception/InvalidSignatureException.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception occurs when an invalid signature is encountered - /// - /// - public class InvalidSignatureException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - public InvalidSignatureException() :base("Invalid signature found") - { - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/InvalidTailTransactionException.cs b/IotaApi.Standard/Exception/InvalidTailTransactionException.cs deleted file mode 100644 index 701f1b3..0000000 --- a/IotaApi.Standard/Exception/InvalidTailTransactionException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception is thrown when an invalid tail transaction was encountered - /// - /// - public class InvalidTailTransactionException : System.Exception - { - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/InvalidTryteException.cs b/IotaApi.Standard/Exception/InvalidTryteException.cs deleted file mode 100644 index 8ea9ac8..0000000 --- a/IotaApi.Standard/Exception/InvalidTryteException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception occurs when invalid trytes are encountered - /// - public class InvalidTryteException : System.Exception - { - - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/InvisibleBundleTransactionException.cs b/IotaApi.Standard/Exception/InvisibleBundleTransactionException.cs deleted file mode 100644 index 4665a41..0000000 --- a/IotaApi.Standard/Exception/InvisibleBundleTransactionException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception occurs when a bundle or transaction is not visible in the tangle - /// - /// - public class InvisibleBundleTransactionException : System.Exception - { - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/IotaApiException.cs b/IotaApi.Standard/Exception/IotaApiException.cs deleted file mode 100644 index d8e7a50..0000000 --- a/IotaApi.Standard/Exception/IotaApiException.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception encapsulates an error that occured while communicating with the node (for example during a core API call) - /// - /// - public class IotaApiException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The error. - public IotaApiException(string error) : base(error) - { - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Exception/NotEnoughBalanceException.cs b/IotaApi.Standard/Exception/NotEnoughBalanceException.cs deleted file mode 100644 index b15aee2..0000000 --- a/IotaApi.Standard/Exception/NotEnoughBalanceException.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Iota.Api.Standard.Exception -{ - /// - /// This exception occurs when a transfer fails because their is not enough balance to perform the transfer - /// - /// - public class NotEnoughBalanceException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - public NotEnoughBalanceException() : base("Not enough balance") - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The total value. - public NotEnoughBalanceException(long totalValue) : base("Not enough balance to transfer " + totalValue + " iota") - { - - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/IotaApi.Standard.csproj b/IotaApi.Standard/IotaApi.Standard.csproj deleted file mode 100644 index c84f7fb..0000000 --- a/IotaApi.Standard/IotaApi.Standard.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard2.0 - Iota.Api.Standard - Iota.Api.Standard - - - - - - - - - - diff --git a/IotaApi.Standard/IotaApi.cs b/IotaApi.Standard/IotaApi.cs deleted file mode 100644 index b4cf883..0000000 --- a/IotaApi.Standard/IotaApi.cs +++ /dev/null @@ -1,1125 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Iota.Api.Standard.Core; -using Iota.Api.Standard.Exception; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Pow; -using Iota.Api.Standard.Utils; - -namespace Iota.Api.Standard -{ - /// - /// This class provides access to the core API methods and the proposed calls - /// - public class IotaApi : IotaCoreApi - { - private readonly ICurl _curl; - - /// - /// Creates an api object that uses the specified connection settings to connect to a node - /// - /// hostname or API address of a node to interact with - /// tcp/udp port - public IotaApi(string host, int port) : this(host, port, new Kerl()) - { - } - - /// - /// Creates an api object that uses the specified connection settings to connect to a node - /// - /// hostname or API address of a node to interact with - /// tcp/udp port - /// - /// a custom curl implementation to be used to perform the pow. Use the other constructor in order to - /// use the default curl implementation provided by the library - /// - public IotaApi(string host, int port, ICurl curl) : base(host, port) - { - _curl = curl ?? throw new ArgumentNullException(nameof(curl)); - } - - /// - /// Gets all possible inputs of a seed and returns them with the total balance. - /// This is either done deterministically (by genearating all addresses until findTransactions is empty and doing - /// getBalances), - /// or by providing a key range to use for searching through. - /// - /// Tryte-encoded seed. It should be noted that this seed is not transferred - /// The Security level of private key / seed. - /// Starting key index - /// Ending key index - /// The minimum threshold of accumulated balances from the inputs that is required - /// The inputs (see ) - public Inputs GetInputs(string seed, int security, int start, int end, long threshold) - { - InputValidator.CheckIfValidSeed(seed); - - seed = InputValidator.PadSeedIfNecessary(seed); - - if (security < 1) - throw new ArgumentException("invalid security level provided"); - - // If start value bigger than end, return error - if (start > end) - throw new ArgumentException("start must be smaller than end", nameof(start)); - - // or if difference between end and start is bigger than 500 keys - if (end - start > 500) - throw new ArgumentException("total number of keys exceeded 500"); - - // Case 1: start and end - // - // If start and end is defined by the user, simply iterate through the keys - // and call getBalances - if (end != 0) - { - var allAddresses = new string[end - start]; - - for (var i = start; i < end; i++) - { - var address = IotaApiUtils.NewAddress(seed, security, i, false, _curl); - allAddresses[i] = address; - } - - return GetBalanceAndFormat(allAddresses, threshold, start, security); - } - - // Case 2: iterate till threshold || end - // - // Either start from index: 0 or start (if defined) until threshold is reached. - // Calls getNewAddress and deterministically generates and returns all addresses - // We then do getBalance, format the output and return it - - var addresses = GetNewAddress(seed, security, start, false, 0, true); - return GetBalanceAndFormat(addresses, threshold, start, security); - } - - /// - /// Gets the balances of the specified addresses and calculates the total balance till the threshold is reached. - /// - /// addresses - /// the threshold - /// start index - /// - /// an Inputs object - /// - /// is thrown if threshold exceeds the sum of balance of the specified - /// addresses - /// - private Inputs GetBalanceAndFormat( - string[] addresses, - long threshold, int start, - int security) - { - if (security < 1) - throw new ArgumentException("invalid security level provided"); - - var getBalancesResponse = GetBalances(addresses.ToList(), 100); - - var balances = getBalancesResponse.Balances; - - var inputs = new Inputs {InputsList = new List(), TotalBalance = 0}; - - var threshholdReached = threshold == 0; - - for (var i = 0; i < addresses.Length; i++) - if (balances[i] > 0) - { - inputs.InputsList.Add(new Input - { - Address = addresses[i], - Balance = balances[i], - KeyIndex = start + i, - Security = security - }); - - inputs.TotalBalance += balances[i]; - - if (inputs.TotalBalance >= threshold) - { - threshholdReached = true; - break; - } - } - - if (threshholdReached) - return inputs; - - - throw new NotEnoughBalanceException(); - } - - /// - /// Main purpose of this function is to get an array of transfer objects as input, and then prepare the transfer by - /// generating the correct bundle, - /// as well as choosing and signing the inputs if necessary (if it's a value transfer). The output of this function is - /// an array of the raw transaction data (trytes) - /// - /// 81-tryte encoded address of recipient - /// - /// the transfers to prepare - /// Optional (default null). The inputs - /// - /// Optional (default null). if defined, this address will be used for sending the remainder - /// value (of the inputs) to. - /// - /// - /// a list containing the trytes of the new bundle - public List PrepareTransfers( - string seed, int security, - Transfer[] transfers, - string remainderAddress, - List inputs, - bool validateInputs) - { - // validate seed - if (!InputValidator.IsValidSeed(seed)) - throw new IllegalStateException("Invalid seed provided."); - - - if(security<1) - throw new ArgumentException("Invalid security level provided."); - - // Input validation of transfers object - InputValidator.CheckTransferArray(transfers); - - // Create a new bundle - var bundle = new Bundle(); - var signatureFragments = new List(); - - long totalValue = 0; - var tag = ""; - - // - // Iterate over all transfers, get totalValue - // and prepare the signatureFragments, message and tag - // - foreach (var transfer in transfers) - { - // remove the checksum of the address if provided - transfer.Address = transfer.Address.RemoveChecksum(); - - var signatureMessageLength = 1; - - // If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction) - if (transfer.Message.Length > Constants.MessageLength) - { - // Get total length, message / maxLength (2187 trytes) - signatureMessageLength += (int) Math.Floor((double) transfer.Message.Length / Constants.MessageLength); - - var msgCopy = transfer.Message; - - // While there is still a message, copy it - while (!string.IsNullOrEmpty(msgCopy)) - { - var fragment = msgCopy.Substring(0, 2187 > msgCopy.Length ? msgCopy.Length : 2187); - msgCopy = msgCopy.Substring(2187, msgCopy.Length - 2187); - - // Pad remainder of fragment - while (fragment.Length < 2187) fragment += '9'; - - signatureFragments.Add(fragment); - } - } - else - { - // Else, get single fragment with 2187 of 9's trytes - var fragment = string.Empty; - - if (!string.IsNullOrEmpty(transfer.Message)) - fragment = transfer.Message.Substring(0, - transfer.Message.Length < 2187 ? transfer.Message.Length : 2187); - - while (fragment.Length < 2187) fragment += '9'; - - signatureFragments.Add(fragment); - } - - // get current timestamp in seconds - var timestamp = (long)Math.Floor((double)IotaApiUtils.CreateTimeStampNow()/1000); - - // If no tag defined, get 27 tryte tag. - - tag = string.IsNullOrEmpty(transfer.Tag) ? "999999999999999999999999999" : transfer.Tag; - - - // Pad for required 27 tryte length - while (tag.Length < 27) tag += '9'; - - - // Add first entries to the bundle - // Slice the address in case the user provided a checksummed one - bundle.AddEntry(signatureMessageLength, transfer.Address, transfer.Value, tag, timestamp); - // Sum up total value - totalValue += transfer.Value; - } - - // Get inputs if we are sending tokens - if (totalValue != 0) - if (inputs != null && inputs.Count > 0) - { - // Get list if addresses of the provided inputs - var inputAddresses = new List(); - foreach (var input in inputs) inputAddresses.Add(input.Address); - - var balances = GetBalances(inputAddresses, 100); - - var confirmedInputs = new List(); - - long totalBalance = 0; - for (var i = 0; i < balances.Balances.Count; i++) - { - var thisBalance = balances.Balances[i]; - totalBalance += thisBalance; - - // If input has balance, add it to confirmedInputs - if (thisBalance > 0) - { - var inputEl = inputs[i]; - inputEl.Balance = thisBalance; - - confirmedInputs.Add(inputEl); - } - } - - // Return not enough balance error - if (totalValue > totalBalance) throw new NotEnoughBalanceException(totalValue); - - return AddRemainder(seed, security, confirmedInputs, bundle, tag, totalValue, remainderAddress, - signatureFragments); - } - - // Case 2: Get inputs deterministically - // - // If no inputs provided, derive the addresses from the seed and - // confirm that the inputs exceed the threshold - else - { - var inputList = GetInputs(seed, security, 0, 0, (int) totalValue).InputsList; - return AddRemainder(seed, security, inputList, bundle, tag, totalValue, remainderAddress, - signatureFragments); - } - - // If no input required, don't sign and simply finalize the bundle - bundle.FinalizeBundle(_curl.Clone()); - bundle.AddTrytes(signatureFragments); - - var bundleTrytes = new List(); - bundle.Transactions.ForEach(tx => bundleTrytes.Add(tx.ToTransactionTrytes())); - - bundleTrytes.Reverse(); - return bundleTrytes; - } - - - private List AddRemainder( - string seed, - int security, - List inputs, - Bundle bundle, - string tag, - long totalValue, - string remainderAddress, - List signatureFragments) - { - var totalTransferValue = totalValue; - - foreach (var input in inputs) - { - var thisBalance = input.Balance; - var toSubtract = 0 - thisBalance; - var timestamp = IotaApiUtils.CreateTimeStampNow(); - - // Add input as bundle entry - bundle.AddEntry(security, input.Address, toSubtract, tag, timestamp); - // If there is a remainder value - // Add extra output to send remaining funds to - - if (thisBalance >= totalTransferValue) - { - var remainder = thisBalance - totalTransferValue; - - // If user has provided remainder address - // Use it to send remaining funds to - if (remainder > 0 && remainderAddress != null) - { - // Remainder bundle entry - bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp); - - // function for signing inputs - IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, _curl); - } - else if (remainder > 0) - { - // Generate a new Address by calling getNewAddress - // ReSharper disable RedundantArgumentDefaultValue - var address = GetNewAddress(seed, security, 0, false, 0, false)[0]; - // ReSharper restore RedundantArgumentDefaultValue - - // Remainder bundle entry - bundle.AddEntry(1, address, remainder, tag, timestamp); - - // function for signing inputs - return IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, _curl); - } - else - { - // If there is no remainder, do not add transaction to bundle - // simply sign and return - return IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, _curl); - } - } - // If multiple inputs provided, subtract the totalTransferValue by - // the inputs balance - else - { - totalTransferValue -= thisBalance; - } - } - - throw new NotEnoughBalanceException(totalValue); - } - - - /// - /// Generates a new address from a seed and returns the remainderAddress. This is either done deterministically, or by - /// providing the index of the new remainderAddress - /// - /// Tryte-encoded seed. It should be noted that this seed is not transferred - /// - /// - /// Optional (default null). Key index to start search from. If the index is provided, the generation - /// of the address is not deterministic. - /// - /// Optional (default false). Adds 9-tryte address checksum - /// Optional (default 1)Total number of addresses to generate. - /// - /// If true, it returns all addresses which were deterministically generated (until - /// findTransactions returns null) - /// - /// an array of strings with the specifed number of addresses - public string[] GetNewAddress(string seed, int security, int index = 0, bool checksum = false, int total = 0, - bool returnAll = false) - { - var allAdresses = new List(); - - // TODO make two different functions out of this - - // Case 1: total - // - // If total number of addresses to generate is supplied, simply generate - // and return the list of all addresses - if (total > 0) - { - // Increase index with each iteration - for (var i = index; i < index + total; i++) - allAdresses.Add(IotaApiUtils.NewAddress(seed, security, i, checksum, new Kerl())); - - return allAdresses.ToArray(); - } - - // Case 2: no total provided - // - // Continue calling findTransactions to see if address was already created - // if null, return list of addresses - // - - var addresses = new List(); - - for (var i = index;; i++) - { - var newAddress = IotaApiUtils.NewAddress(seed, security, i, checksum, new Kerl()); - var response = FindTransactionsByAddresses(newAddress); - - if (returnAll) addresses.Add(newAddress); - - if (response.Hashes.Count == 0) - break; - } - - return addresses.ToArray(); - } - - /// - /// Gets the transfers which are associated with a seed. - /// The transfers are determined by either calculating deterministically which addresses were already used, - /// or by providing a list of indexes to get the transfers from. - /// - /// tryte-encoded seed. It should be noted that this seed is not transferred - /// If True, it gets the inclusion states of the transfers. - /// - /// the address start index - /// the address end index - /// An Array of Bundle object that represent the transfers - public Bundle[] GetTransfers(string seed, int security, int? start, int? end, bool inclusionStates = false) - { - InputValidator.CheckIfValidSeed(seed); - seed = InputValidator.PadSeedIfNecessary(seed); - - if (!start.HasValue) - start = 0; - if (!end.HasValue) - end = 0; - - // If start value bigger than end, return error - // or if difference between end and start is bigger than 500 keys - if (start.Value > end.Value || end.Value > start + 500) - throw new System.Exception("Invalid inputs provided: start, end"); - - // first call findTransactions - // If a transaction is non tail, get the tail transactions associated with it - // add it to the list of tail transactions - - var addresses = GetNewAddress(seed, security, start.Value, false, - end.Value, true); - - - var bundles = BundlesFromAddresses(addresses, inclusionStates); - return bundles; - } - - private Bundle[] BundlesFromAddresses(string[] addresses, bool inclusionStates) - { - var trxs = FindTransactionObjects(addresses); - // set of tail transactions - var tailTransactions = new List(); - var nonTailBundleHashes = new List(); - - foreach (var trx in trxs) - // Sort tail and nonTails - if (trx.CurrentIndex == 0) - { - tailTransactions.Add(trx.Hash); - } - else - { - if (nonTailBundleHashes.IndexOf(trx.Bundle) == -1) nonTailBundleHashes.Add(trx.Bundle); - } - - var bundleObjects = FindTransactionObjectsByBundle(nonTailBundleHashes.ToArray()); - foreach (var trx in bundleObjects) - // Sort tail and nonTails - if (trx.CurrentIndex == 0) - if (tailTransactions.IndexOf(trx.Hash) == -1) - tailTransactions.Add(trx.Hash); - - var finalBundles = new List(); - var tailTxArray = tailTransactions.ToArray(); - - // If inclusionStates, get the confirmation status - // of the tail transactions, and thus the bundles - GetInclusionStatesResponse gisr = null; - if (inclusionStates) - { - try - { - gisr = GetLatestInclusion(tailTxArray); - } - catch (IllegalAccessError) - { - // suppress exception (the check is done below) - } - - if (gisr == null || gisr.States == null || gisr.States.Count == 0) - throw new ArgumentException("Inclusion states not found"); - } - - - var finalInclusionStates = gisr; - - Parallel.ForEach(tailTransactions, param => - { - try - { - var b = GetBundle(param); - - if (inclusionStates) - { - var thisInclusion = finalInclusionStates != null && - finalInclusionStates.States[tailTxArray.ToList().IndexOf(param)]; - foreach (var t in b.Transactions) t.Persistance = thisInclusion; - } - - finalBundles.Add(b); - } - catch (System.Exception ex) - { - Console.WriteLine("Bundle error: " + ex.Message); - } - }); - - finalBundles.Sort(); - var returnValue = new Bundle[finalBundles.Count]; - for (var i = 0; i < finalBundles.Count; i++) - returnValue[i] = new Bundle(finalBundles[i].Transactions, finalBundles[i].Transactions.Count); - return returnValue; - } - - /// - /// Finds the transaction objects. - /// - /// The addresses. - /// a list of transactions - public List FindTransactionObjects(string[] addresses) - { - var addressesWithoutChecksum = - addresses.Select(address => address.RemoveChecksum()).ToList(); - - var ftr = FindTransactions(addressesWithoutChecksum, null, null, null); - if (ftr?.Hashes == null) - return null; - - // get the transaction objects of the transactions - return GetTransactionsObjects(ftr.Hashes.ToArray()); - } - - /// - /// Gets the transactions objects. - /// - /// The hashes in trytes - /// a list of transactions - public List GetTransactionsObjects(string[] hashes) - { - if (!InputValidator.IsArrayOfHashes(hashes)) - throw new IllegalStateException("Not an Array of Hashes: " + hashes); - - var trytesResponse = GetTrytes(hashes); - - var trxs = new List(); - - foreach (var tryte in trytesResponse.Trytes) trxs.Add(new Transaction(tryte, _curl)); - return trxs; - } - - /// - /// Finds the transaction objects by bundle. - /// - /// The bundles. - /// a list of Transaction objects - public List FindTransactionObjectsByBundle(string[] bundles) - { - var ftr = FindTransactions(null, null, null, bundles.ToList()); - if (ftr == null || ftr.Hashes == null) - return null; - - // get the transaction objects of the transactions - return GetTransactionsObjects(ftr.Hashes.ToArray()); - } - - - /// - /// Replays the bundle. - /// - /// The transaction. - /// The depth. - /// The minimum weight magnitude. - /// an array of boolean that indicate which transactions have been replayed successfully - public bool[] ReplayBundle(string transaction, int depth, int minWeightMagnitude) - { - //StopWatch stopWatch = new StopWatch(); - - var bundleTrytes = new List(); - - var bundle = GetBundle(transaction); - - bundle.Transactions.ForEach(t => bundleTrytes.Add(t.ToTransactionTrytes())); - - var trxs = SendTrytes(bundleTrytes.ToArray(), depth, minWeightMagnitude).ToList(); - - var successful = new bool[trxs.Count]; - - for (var i = 0; i < trxs.Count; i++) - { - var response = FindTransactionsByBundles(trxs[i].Bundle); - successful[i] = response.Hashes.Count != 0; - } - - return successful; - } - - /// - /// Finds the transactions by bundles. - /// - /// The bundles. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByBundles(params string[] bundles) - { - return FindTransactions(null, null, null, bundles.ToList()); - } - - /// - /// Finds the transactions by approvees. - /// - /// The approvees. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByApprovees(params string[] approvees) - { - return FindTransactions(null, null, approvees.ToList(), null); - } - - - /// - /// Finds the transactions by digests. - /// - /// The bundles. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByDigests(params string[] bundles) - { - return FindTransactions(null, bundles.ToList(), null, null); - } - - /// - /// Finds the transactions by addresses. - /// - /// The addresses. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByAddresses(params string[] addresses) - { - var addressesWithoutChecksum = new List(); - foreach (var address in addresses) - { - var address0 = address.RemoveChecksum(); - addressesWithoutChecksum.Add(address0); - } - - return FindTransactions(addressesWithoutChecksum, null, null, null); - } - - /// - /// Gets the latest inclusion. - /// - /// The hashes. - /// a GetInclusionStatesResponse cotaining the inclusion state of the specified hashes - public GetInclusionStatesResponse GetLatestInclusion(string[] hashes) - { - string[] latestMilestone = {GetNodeInfo().LatestSolidSubtangleMilestone}; - return GetInclusionStates(hashes, latestMilestone); - } - - - /// - /// Wrapper function that basically does prepareTransfers, as well as attachToTangle and finally, it broadcasts and - /// stores the transactions locally. - /// - /// tryte-encoded seed - /// - /// depth - /// The minimum weight magnitude - /// Array of transfer objects - /// Optional (default null). List of inputs used for funding the transfer - /// - /// Optional (default null). If defined, this address will be used for sending the remainder value - /// (of the inputs) to - /// - /// - /// - /// an array of the boolean that indicates which Transactions where sent successully - public bool[] SendTransfer( - string seed, int security, int depth, - int minWeightMagnitude, Transfer[] transfers, - Input[] inputs, string remainderAddress, - bool validateInputs, bool validateInputAddresses) - { - var trytes = PrepareTransfers(seed, security, transfers, - remainderAddress, inputs?.ToList(), validateInputs); - var trxs = SendTrytes(trytes.ToArray(), depth, minWeightMagnitude); - - var successful = new bool[trxs.Length]; - - for (var i = 0; i < trxs.Length; i++) - { - var response = FindTransactionsByBundles(trxs[i].Bundle); - - successful[i] = response.Hashes.Count != 0; - } - - return successful; - } - - /// - /// Sends the trytes. - /// - /// The trytes. - /// The depth. - /// Optional (default 14). The minimum weight magnitude. - /// an Array of Transactions - public Transaction[] SendTrytes(string[] trytes, int depth, int minWeightMagnitude = 14) - { - var transactionsToApproveResponse = GetTransactionsToApprove(depth); - - var attachToTangleResponse = - AttachToTangle(transactionsToApproveResponse.TrunkTransaction, - transactionsToApproveResponse.BranchTransaction, trytes, minWeightMagnitude); - try - { - BroadcastAndStore(attachToTangleResponse.Trytes); - } - catch (System.Exception) - { - return new Transaction[0]; - } - - var trx = new List(); - - foreach (var tx in attachToTangleResponse.Trytes) trx.Add(new Transaction(tx, _curl)); - return trx.ToArray(); - } - - /// - /// This function returns the bundle which is associated with a transaction. Input can by any type of transaction (tail - /// and non-tail). - /// If there are conflicting bundles (because of a replay for example) it will return multiple bundles. - /// It also does important validation checking (signatures, sum, order) to ensure that the correct bundle is returned. - /// - /// the transaction encoded in trytes - /// an array of bundle, if there are multiple arrays it means that there are conflicting bundles. - public Bundle GetBundle(string transaction) - { - if (!InputValidator.IsHash(transaction)) - { - throw new ArgumentException("Invalid hashes provided."); - } - - var bundle = TraverseBundle(transaction, null, new Bundle()); - - if (bundle == null) - throw new ArgumentException("Unknown Bundle"); - - long totalSum = 0; - var bundleHash = bundle.Transactions[0].Bundle; - - ICurl curl = new Kerl(); - curl.Reset(); - - var signaturesToValidate = new List(); - - for (var index = 0; index < bundle.Transactions.Count; index++) - { - var bundleTransaction = bundle.Transactions[index]; - var bundleValue = bundleTransaction.Value; - totalSum += bundleValue; - - if (bundleTransaction.CurrentIndex != index) - throw new InvalidBundleException("The index of the bundle " + bundleTransaction.CurrentIndex + - " did not match the expected index " + index); - - // Get the transaction trytes - var thisTxTrytes = bundleTransaction.ToTransactionTrytes().Substring(2187, 162); - - // Absorb bundle hash + value + timestamp + lastIndex + currentIndex trytes. - curl.Absorb(Converter.ToTrits(thisTxTrytes)); - - // Check if input transaction - if (bundleValue < 0) - { - var address = bundleTransaction.Address; - var sig = new Signature {Address = address}; - sig.SignatureFragments.Add(bundleTransaction.SignatureMessageFragment); - - // Find the subsequent txs with the remaining signature fragment - for (var i = index + 1; i < bundle.Transactions.Count; i++) - { - var newBundleTx = bundle.Transactions[i]; - - // Check if new tx is part of the signature fragment - if (newBundleTx.Address == address && newBundleTx.Value == 0) - { - if (sig.SignatureFragments.IndexOf(newBundleTx.SignatureMessageFragment) == -1) - sig.SignatureFragments.Add(newBundleTx.SignatureMessageFragment); - } - - } - - signaturesToValidate.Add(sig); - } - } - - // Check for total sum, if not equal 0 return error - if (totalSum != 0) - throw new InvalidBundleException("Invalid Bundle Sum"); - - var bundleFromTrxs = new int[243]; - curl.Squeeze(bundleFromTrxs); - var bundleFromTxString = Converter.ToTrytes(bundleFromTrxs); - - // Check if bundle hash is the same as returned by tx object - if (!bundleFromTxString.Equals(bundleHash)) - throw new InvalidBundleException("Invalid Bundle Hash"); - // Last tx in the bundle should have currentIndex === lastIndex - bundle.Length = bundle.Transactions.Count; - if ( - !bundle.Transactions[bundle.Length - 1].CurrentIndex.Equals( - bundle.Transactions[bundle.Length - 1].LastIndex)) - throw new InvalidBundleException("Invalid Bundle"); - - // Validate the signatures - foreach (var aSignaturesToValidate in signaturesToValidate) - { - var signatureFragments = aSignaturesToValidate.SignatureFragments.ToArray(); - var address = aSignaturesToValidate.Address; - var isValidSignature = new Signing().ValidateSignatures(address, signatureFragments, bundleHash); - - if (!isValidSignature) - throw new InvalidSignatureException(); - } - - return bundle; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public AccountData GetAccountData(String seed, int security, int index, bool checksum, int total, - bool returnAll, int start, int end, bool inclusionStates, long threshold) - { - - if (start > end || end > (start + 1000)) - { - throw new ArgumentException("Invalid input provided."); - } - - var addresses = GetNewAddress(seed, security, index, checksum, total, returnAll); - var bundle = GetTransfers(seed, security, start, end, inclusionStates); - var inputs = GetInputs(seed, security, start, end, threshold); - - return new AccountData(new List(addresses), bundle, inputs.InputsList, inputs.TotalBalance); - } - - /// - /// Wrapper function that broadcasts and stores the specified trytes - /// - /// trytes - public void BroadcastAndStore(List trytes) - { - BroadcastTransactions(trytes); - StoreTransactions(trytes); - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public List InitiateTransfer( - int securitySum, string inputAddress, string remainderAddress, - List transfers, bool testMode) - { - // validate input address - if (!InputValidator.IsAddress(inputAddress)) - throw new ArgumentException("Invalid addresses provided."); - - // validate remainder address - if (remainderAddress != null && !InputValidator.IsAddress(remainderAddress)) - { - throw new ArgumentException("Invalid addresses provided."); - } - - // Input validation of transfers object - if (!InputValidator.IsTransfersCollectionValid(transfers)) - { - throw new ArgumentException("Invalid transfers provided."); - } - - // Create a new bundle - Bundle bundle = new Bundle(); - - long totalValue = 0; - List signatureFragments = new List(); - String tag = ""; - // - - // Iterate over all transfers, get totalValue - // and prepare the signatureFragments, message and tag - foreach (Transfer transfer in transfers) - { - - // remove the checksum of the address if provided - if (transfer.Address.IsValidChecksum()) - { - transfer.Address = transfer.Address.RemoveChecksum(); - } - - int signatureMessageLength = 1; - - // If message longer than 2187 trytes, increase signatureMessageLength (add next transaction) - if (transfer.Message.Length > Constants.MessageLength) - { - - // Get total length, message / maxLength (2187 trytes) - signatureMessageLength += (int)Math.Floor((double)transfer.Message.Length / Constants.MessageLength); - - String msgCopy = transfer.Message; - - // While there is still a message, copy it - - while (!string.IsNullOrEmpty(msgCopy)) - { - - string fragment = msgCopy.Substring(0, Constants.MessageLength); - msgCopy = msgCopy.Substring(Constants.MessageLength, msgCopy.Length - Constants.MessageLength); - - // Pad remainder of fragment - fragment = fragment.PadRight(Constants.MessageLength, '9'); - - - signatureFragments.Add(fragment); - } - - } - else - { - - // Else, get single fragment with 2187 of 9's trytes - String fragment = transfer.Message; - - if (transfer.Message.Length < Constants.MessageLength) - { - fragment = fragment.PadRight(Constants.MessageLength, '9'); - } - - signatureFragments.Add(fragment); - - } - - tag = transfer.Tag; - - // pad for required 27 tryte length - if (transfer.Tag.Length < Constants.TagLength) - { - tag = tag.PadRight(Constants.TagLength, '9'); - } - - // get current timestamp in seconds - long timestamp = (long)Math.Floor(GetCurrentTimestampInSeconds()); - - // Add first entry to the bundle - bundle.AddEntry(signatureMessageLength, transfer.Address, transfer.Value, tag, timestamp); - // Sum up total value - totalValue += transfer.Value; - } - - // Get inputs if we are sending tokens - if (totalValue != 0) - { - GetBalancesResponse balancesResponse = GetBalances(new List { inputAddress }, 100); - var balances = balancesResponse.Balances; - - long totalBalance = 0; - - foreach (var balance in balances) - { - totalBalance += balance; - } - - // get current timestamp in seconds - long timestamp = (long)Math.Floor(GetCurrentTimestampInSeconds()); - - // bypass the balance checks during unit testing - if (testMode) - totalBalance += 1000; - - if (totalBalance > 0) - { - - long toSubtract = 0 - totalBalance; - - // Add input as bundle entry - // Only a single entry, signatures will be added later - bundle.AddEntry(securitySum, inputAddress, toSubtract, tag, timestamp); - } - // Return not enough balance error - if (totalValue > totalBalance) - { - throw new IllegalStateException("Not enough balance."); - } - - // If there is a remainder value - // Add extra output to send remaining funds to - if (totalBalance > totalValue) - { - - long remainder = totalBalance - totalValue; - - // Remainder bundle entry if necessary - if (remainderAddress == null) - { - throw new IllegalStateException("No remainder address defined."); - } - - bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp); - } - - bundle.FinalizeBundle(new Curl(CurlMode.CurlP81)); - bundle.AddTrytes(signatureFragments); - - return bundle.Transactions; - } - else - { - throw new System.Exception("Invalid value transfer: the transfer does not require a signature."); - } - } - - private Bundle TraverseBundle(string trunkTransaction, string bundleHash, Bundle bundle) - { - var gtr = GetTrytes(trunkTransaction); - - if (gtr.Trytes.Count == 0) - throw new InvisibleBundleTransactionException(); - - var trytes = gtr.Trytes[0]; - - var transaction = new Transaction(trytes, _curl); - - // If first transaction to search is not a tail, return error - if (bundleHash == null && transaction.CurrentIndex != 0) throw new InvalidTailTransactionException(); - - // If no bundle hash, define it - if (bundleHash == null) bundleHash = transaction.Bundle; - - // If different bundle hash, return with bundle - if (bundleHash != transaction.Bundle) return bundle; - - // If only one bundle element, return - if (transaction.LastIndex == 0 && transaction.CurrentIndex == 0) - return new Bundle(new List { transaction }, 1); - - // Define new trunkTransaction for search - var trunkTx = transaction.TrunkTransaction; - - // Add transaction object to bundle - bundle.Transactions.Add(transaction); - - // Continue traversing with new trunkTx - return TraverseBundle(trunkTx, bundleHash, bundle); - } - - private double GetCurrentTimestampInSeconds() - { - DateTime now = DateTime.UtcNow; - DateTime epoch = new DateTime - (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - return (now - epoch).TotalSeconds; - } - } - -} \ No newline at end of file diff --git a/IotaApi.Standard/Model/AccountData.cs b/IotaApi.Standard/Model/AccountData.cs deleted file mode 100644 index 2099d85..0000000 --- a/IotaApi.Standard/Model/AccountData.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Model -{ - /// - /// - /// - public class AccountData - { - /// - /// - /// - /// - /// - /// - /// - public AccountData(List addresses, - Bundle[] transferBundle, List inputList, long totalBalance) - { - Addresses = addresses; - TransferBundle = transferBundle; - InputList = inputList; - TotalBalance = totalBalance; - } - - /// - /// - /// - public List Addresses { get; set; } - - /// - /// - /// - public Bundle[] TransferBundle { get; set; } - - /// - /// - /// - public List InputList { get; set; } - - /// - /// - /// - public long TotalBalance { get; set; } - } -} diff --git a/IotaApi.Standard/Model/Bundle.cs b/IotaApi.Standard/Model/Bundle.cs deleted file mode 100644 index fcb15bf..0000000 --- a/IotaApi.Standard/Model/Bundle.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; -using System.Collections.Generic; -using Iota.Api.Standard.Pow; -using Iota.Api.Standard.Utils; - -namespace Iota.Api.Standard.Model -{ - /// - /// This class represents a Bundle, a set of transactions - /// - public class Bundle : IComparable - { - /// - /// Initializes a new instance of the class without transactions. - /// - public Bundle() : this(new List(), 0) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The transactions. - /// The length. - public Bundle(List transactions, int length) - { - Transactions = transactions; - Length = length; - } - - /// - /// Gets the at the specified index. - /// - /// - /// The . - /// - /// The index. - /// - public Transaction this[int index] => Transactions[index]; - - /// - /// Gets or sets the transactions. - /// - /// - /// The transactions. - /// - public List Transactions { get; set; } - - /// - /// Gets or sets the length of the bundle - /// - /// - /// The length. - /// - public int Length { get; set; } - - /// - /// Compares the current object with another object of the same type. - /// - /// An object to compare with this object. - /// - /// A value that indicates the relative order of the objects being compared. The return value has the following - /// meanings: Value Meaning Less than zero This object is less than the parameter.Zero This - /// object is equal to . Greater than zero This object is greater than - /// . - /// - public int CompareTo(Bundle other) - { - var timeStamp1 = Transactions[0].Timestamp; - var timeStamp2 = other.Transactions[0].Timestamp; - - if (timeStamp1 < timeStamp2) - return -1; - if (timeStamp1 > timeStamp2) - return 1; - return 0; - } - - /// - /// Adds a bundle entry - /// - /// Length of the signature message. - /// The address. - /// The value. - /// The tag. - /// The timestamp. - public void AddEntry(int signatureMessageLength, string address, long value, string tag, long timestamp) - { - for (var i = 0; i < signatureMessageLength; i++) - { - var trx = new Transaction(address, i == 0 ? value : 0, tag, timestamp); - Transactions.Add(trx); - } - } - - /// - /// Adds the trytes. - /// - /// The signature fragments. - public void AddTrytes(List signatureFragments) - { - var emptySignatureFragment = ""; - var emptyHash = Constants.EmptyHash; - long emptyTimestamp = 999999999L; - - while (emptySignatureFragment.Length < 2187) emptySignatureFragment += '9'; - - for (var i = 0; i < Transactions.Count; i++) - { - var transaction = Transactions[i]; - - // Fill empty signatureMessageFragment - transaction.SignatureMessageFragment = signatureFragments.Count <= i || - string.IsNullOrEmpty(signatureFragments[i]) - ? emptySignatureFragment - : signatureFragments[i]; - // Fill empty trunkTransaction - transaction.TrunkTransaction = emptyHash; - - // Fill empty branchTransaction - transaction.BranchTransaction = emptyHash; - - transaction.AttachmentTimestamp = emptyTimestamp; - transaction.AttachmentTimestampLowerBound = emptyTimestamp; - transaction.AttachmentTimestampUpperBound = emptyTimestamp; - // Fill empty nonce - transaction.Nonce = "999999999999999999999999999"; - } - } - - - /// - /// Normalizeds the bundle. - /// - /// The bundle hash. - /// - public int[] NormalizedBundle(string bundleHash) - { - var normalizedBundle = new int[81]; - - for (var i = 0; i < 3; i++) - { - long sum = 0; - for (var j = 0; j < 27; j++) - sum += - normalizedBundle[i * 27 + j] = - Converter.ToValue(Converter.ToTrits("" + bundleHash[i * 27 + j])); - - if (sum >= 0) - { - while (sum-- > 0) - for (var j = 0; j < 27; j++) - if (normalizedBundle[i * 27 + j] > -13) - { - normalizedBundle[i * 27 + j]--; - break; - } - } - else - { - while (sum++ < 0) - for (var j = 0; j < 27; j++) - if (normalizedBundle[i * 27 + j] < 13) - { - normalizedBundle[i * 27 + j]++; - break; - } - } - } - - return normalizedBundle; - } - - - /// - /// Finalizes the bundle using the specified curl implementation - /// - /// The custom curl. - public void FinalizeBundle(ICurl customCurl) - { - customCurl.Reset(); - - for (var i = 0; i < Transactions.Count; i++) - { - var valueTrits = Converter.ToTrits(Transactions[i].Value, 81); - - var timestampTrits = Converter.ToTrits(Transactions[i].Timestamp, 27); - - var currentIndexTrits = Converter.ToTrits(Transactions[i].CurrentIndex = i, 27); - - var lastIndexTrits = Converter.ToTrits( - Transactions[i].LastIndex = Transactions.Count - 1, 27); - - var stringToConvert = Transactions[i].Address - + Converter.ToTrytes(valueTrits) - + Transactions[i].Tag + - Converter.ToTrytes(timestampTrits) - + Converter.ToTrytes(currentIndexTrits) + - Converter.ToTrytes(lastIndexTrits); - - var t = Converter.ToTrits(stringToConvert); - customCurl.Absorb(t, 0, t.Length); - } - - var hash = new int[243]; - customCurl.Squeeze(hash, 0, hash.Length); - var hashInTrytes = Converter.ToTrytes(hash); - - foreach (var transaction in Transactions) transaction.Bundle = hashInTrytes; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Transactions)}: {string.Join(",", Transactions)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Model/Inputs.cs b/IotaApi.Standard/Model/Inputs.cs deleted file mode 100644 index 4e318d8..0000000 --- a/IotaApi.Standard/Model/Inputs.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Iota.Api.Standard.Model -{ - /// - /// This class represents the Inputs - /// - public class Inputs - { - /// - /// Gets or sets the inputs list. - /// - /// - /// The inputs list. - /// - public List InputsList { get; set; } - - /// - /// Gets or sets the total balance. - /// - /// - /// The total balance. - /// - public long TotalBalance { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return - $"Inputs:\n {string.Join(",", InputsList.Select(i => "[" + i + "]" + "\n"))}{nameof(TotalBalance)}: {TotalBalance}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Model/Signature.cs b/IotaApi.Standard/Model/Signature.cs deleted file mode 100644 index 3dc248e..0000000 --- a/IotaApi.Standard/Model/Signature.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Api.Standard.Model -{ - /// - /// Thic class represents a signature - /// - public class Signature - { - /// - /// Initializes a new instance of the class. - /// - public Signature() - { - SignatureFragments = new List(); - } - - /// - /// Gets or sets the address. - /// - /// - /// The address. - /// - public string Address { get; set; } - - /// - /// Gets or sets the signature fragments. - /// - /// - /// The signature fragments. - /// - public List SignatureFragments { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Address)}: {Address}, {nameof(SignatureFragments)}: {string.Join(",", SignatureFragments)}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Model/Transfer.cs b/IotaApi.Standard/Model/Transfer.cs deleted file mode 100644 index 9f3839b..0000000 --- a/IotaApi.Standard/Model/Transfer.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace Iota.Api.Standard.Model -{ - /// - /// This class represents a Transfer - /// - public class Transfer - { - /// - /// Initializes a new instance of the class. - /// - /// The address. - /// The value. - /// The message. - /// The tag. - public Transfer(string address, long value, string message, string tag) - { - Address = address; - Value = value; - Message = message; - Tag = tag; - } - - /// - /// Gets or sets the address. - /// - /// - /// The address. - /// - public string Address { get; set; } - - /// - /// Gets or sets the hash. - /// - /// - /// The hash. - /// - public string Hash { get; set; } - - /// - /// Gets or sets the persistence. - /// - /// - /// The persistence. - /// - public int Persistence { get; set; } - - /// - /// Gets or sets the timestamp. - /// - /// - /// The timestamp. - /// - public string Timestamp { get; set; } - - /// - /// Gets or sets the value. - /// - /// - /// The value. - /// - public long Value { get; set; } - - /// - /// Gets or sets the message. - /// - /// - /// The message. - /// - public string Message { get; set; } - - /// - /// Gets or sets the tag. - /// - /// - /// The tag. - /// - public string Tag { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Address)}: {Address}, {nameof(Hash)}: {Hash}, {nameof(Message)}: {Message}, {nameof(Persistence)}: {Persistence}, {nameof(Tag)}: {Tag}, {nameof(Timestamp)}: {Timestamp}, {nameof(Value)}: {Value}"; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Pow/Curl.cs b/IotaApi.Standard/Pow/Curl.cs deleted file mode 100644 index 43b506c..0000000 --- a/IotaApi.Standard/Pow/Curl.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System; -using System.ComponentModel; -using Iota.Api.Standard.Utils; - -namespace Iota.Api.Standard.Pow -{ - /// - /// (c) 2016 Come-from-Beyond - /// Curl belongs to the sponge function family. - /// - public class Curl : Sponge - { - private const int StateLength = 3 * HashLength; - - private const int NumberOfRoundsP81 = 81; - private const int NumberOfRoundsP27 = 27; - - private static readonly int[] TruthTable = {1, 0, -1, 2, 1, -1, 0, 2, -1, 1, 0}; - private readonly int _numberOfRounds; - private readonly int[] _scratchpad = new int[StateLength]; - private readonly long[] _stateHigh; - private readonly long[] _stateLow; - - /// - /// - /// - /// - /// - public Curl(bool pair, CurlMode mode) - { - switch (mode) - { - case CurlMode.CurlP27: - _numberOfRounds = NumberOfRoundsP27; - break; - case CurlMode.CurlP81: - _numberOfRounds = NumberOfRoundsP81; - break; - default: - throw new InvalidEnumArgumentException("Only Curl-P-27 and Curl-P-81 are supported."); - } - - if (pair) - { - _stateHigh = new long[StateLength]; - _stateLow = new long[StateLength]; - State = null; - Set(); - } - else - { - State = new int[StateLength]; - _stateHigh = null; - _stateLow = null; - } - } - - /// - /// - /// - /// - public Curl(CurlMode mode) - { - switch (mode) - { - case CurlMode.CurlP27: - _numberOfRounds = NumberOfRoundsP27; - break; - case CurlMode.CurlP81: - _numberOfRounds = NumberOfRoundsP81; - break; - default: - throw new InvalidEnumArgumentException("Only Curl-P-27 and Curl-P-81 are supported."); - } - - State = new int[StateLength]; - _stateHigh = null; - _stateLow = null; - } - - /// - /// Gets or sets the state. - /// - /// - /// The state. - /// - public int[] State { get; set; } - - /// - /// Absorbs the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// the ICurl instance (used for method chaining) - public override void Absorb(int[] trits, int offset, int length) - { - do - { - Array.Copy(trits, offset, State, 0, length < HashLength ? length : HashLength); - Transform(); - offset += HashLength; - } while ((length -= HashLength) > 0); - } - - private void Transform() - { - var scratchpadIndex = 0; - - for (var round = 0; round < _numberOfRounds; round++) - { - Array.Copy(State, 0, _scratchpad, 0, StateLength); - for (var stateIndex = 0; stateIndex < StateLength; stateIndex++) - { - var prevScratchpadIndex = scratchpadIndex; - if (scratchpadIndex < 365) - scratchpadIndex += 364; - else - scratchpadIndex += -365; - - State[stateIndex] = - TruthTable[ - _scratchpad[prevScratchpadIndex] - + (_scratchpad[scratchpadIndex] << 2) - + 5]; - } - } - } - - /// - /// Resets this state. - /// - /// - /// the ICurl instance (used for method chaining) - /// - public override void Reset() - { - for (var stateIndex = 0; stateIndex < StateLength; stateIndex++) State[stateIndex] = 0; - } - - /// - /// - /// - /// - public void Reset(bool pair) - { - if (pair) - Set(); - else - Reset(); - } - - /// - /// Squeezes the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// - /// the squeezed trits - /// - public override void Squeeze(int[] trits, int offset, int length) - { - do - { - Array.Copy(State, 0, trits, offset, length < HashLength ? length : HashLength); - Transform(); - offset += HashLength; - } while ((length -= HashLength) > 0); - } - - /// - /// Clones this instance. - /// - /// a new instance - public override ICurl Clone() - { - return new Curl(CurlMode.CurlP81); - } - - /// - /// - /// - /// - /// - public void Absorb(Tuple pair, int offset, int length) - { - int o = offset, l = length; - do - { - Array.Copy(pair.Item1, o, _stateLow, 0, l < HashLength ? l : HashLength); - Array.Copy(pair.Item2, o, _stateHigh, 0, l < HashLength ? l : HashLength); - - PairTransform(); - o += HashLength; - } while ((l -= HashLength) > 0); - } - - /// - /// - /// - /// - /// - /// - public Tuple Squeeze(Tuple pair, int offset, int length) - { - int o = offset, l = length; - var low = pair.Item1; - var hi = pair.Item2; - do - { - Array.Copy(_stateLow, 0, low, o, l < HashLength ? l : HashLength); - Array.Copy(_stateHigh, 0, hi, o, l < HashLength ? l : HashLength); - - PairTransform(); - o += HashLength; - } while ((l -= HashLength) > 0); - - return new Tuple(low, hi); - } - - #region Private - - private void Set() - { - for (var i = 0; i < _stateLow.Length; i++) _stateLow[i] = (long) Converter.HIGH_LONG_BITS; - - for (var i = 0; i < _stateHigh.Length; i++) _stateHigh[i] = (long) Converter.HIGH_LONG_BITS; - } - - private void PairTransform() - { - var curlScratchpadLow = new long[StateLength]; - var curlScratchpadHigh = new long[StateLength]; - var curlScratchpadIndex = 0; - for (var round = _numberOfRounds; round-- > 0;) - { - Array.Copy(_stateLow, 0, curlScratchpadLow, 0, StateLength); - Array.Copy(_stateHigh, 0, curlScratchpadHigh, 0, StateLength); - for (var curlStateIndex = 0; curlStateIndex < StateLength; curlStateIndex++) - { - var alpha = curlScratchpadLow[curlScratchpadIndex]; - var beta = curlScratchpadHigh[curlScratchpadIndex]; - var gamma = curlScratchpadHigh[curlScratchpadIndex += curlScratchpadIndex < 365 ? 364 : -365]; - var delta = (alpha | ~gamma) & (curlScratchpadLow[curlScratchpadIndex] ^ beta); - _stateLow[curlStateIndex] = ~delta; - _stateHigh[curlStateIndex] = (alpha ^ gamma) | delta; - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Pow/CurlMode.cs b/IotaApi.Standard/Pow/CurlMode.cs deleted file mode 100644 index dfe7448..0000000 --- a/IotaApi.Standard/Pow/CurlMode.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Iota.Api.Standard.Pow -{ - /// - /// - /// - public enum CurlMode - { - /// - /// - /// - CurlP81, - /// - /// - /// - CurlP27, - /// - /// - /// - Kerl - } -} diff --git a/IotaApi.Standard/Pow/ICurl.cs b/IotaApi.Standard/Pow/ICurl.cs deleted file mode 100644 index 9232b20..0000000 --- a/IotaApi.Standard/Pow/ICurl.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Iota.Api.Standard.Pow -{ - /// - /// This interface abstracts the curl hashing algorithm - /// - public interface ICurl - { - /// - /// Absorbs the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// the ICurl instance (used for method chaining) - void Absorb(int[] trits, int offset, int length); - - /// - /// Absorbs the specified trits. - /// - /// The trits. - /// the ICurl instance (used for method chaining) - void Absorb(int[] trits); - - /// - /// Squeezes the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// the squeezed trits - void Squeeze(int[] trits, int offset, int length); - - /// - /// Squeezes the specified trits. - /// - /// The trits. - /// the squeezed trits - void Squeeze(int[] trits); - - - /// - /// Resets this state. - /// - /// the ICurl instance (used for method chaining) - void Reset(); - - /// - /// - /// - /// - ICurl Clone(); - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Pow/PearlDiver.cs b/IotaApi.Standard/Pow/PearlDiver.cs deleted file mode 100644 index 7ed9d7a..0000000 --- a/IotaApi.Standard/Pow/PearlDiver.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Iota.Api.Standard.Pow -{ - /// - /// (c) 2016 Come-from-Beyond - /// See https://github.com/iotaledger/iri/blob/dev/src/main/java/com/iota/iri/hash/PearlDiver.java - /// - public class PearlDiver - { - private const int TransactionLength = 8019; - private const int NumberOfRoundsp81 = 81; - - private const int CurlHashLength = 243; - private const int CurlStateLength = CurlHashLength * 3; - - private const long HighBits = -1; - private const long LowBits = 0; - private static readonly object SyncObj = new object(); - - private State _state; - - /// - /// - /// - /// - /// - /// - // ReSharper disable once RedundantAssignment - public bool Search(int[] transactionTrits, int minWeightMagnitude, int numberOfThreads) - { - if (transactionTrits.Length != TransactionLength) - throw new ArgumentException($"Invalid transaction trits length: {transactionTrits.Length}"); - - if (minWeightMagnitude < 0 || minWeightMagnitude > CurlHashLength) - throw new ArgumentException($"Invalid min weight magnitude: {minWeightMagnitude}"); - - lock (SyncObj) - { - _state = State.Running; - } - - var midCurlStateLow = new long[CurlStateLength]; - var midCurlStateHigh = new long[CurlStateLength]; - - // step1 - { - for (var i = CurlHashLength; i < CurlStateLength; i++) - { - midCurlStateLow[i] = HighBits; - midCurlStateHigh[i] = HighBits; - } - - var offset = 0; - var curlScratchpadLowStep1 = new long[CurlStateLength]; - var curlScratchpadHighStep1 = new long[CurlStateLength]; - - for (var i = (TransactionLength - CurlHashLength) / CurlHashLength; i-- > 0;) - { - for (var j = 0; j < CurlHashLength; j++) - switch (transactionTrits[offset++]) - { - case 0: - midCurlStateLow[j] = HighBits; - midCurlStateHigh[j] = HighBits; - break; - case 1: - midCurlStateLow[j] = LowBits; - midCurlStateHigh[j] = HighBits; - break; - default: - midCurlStateLow[j] = HighBits; - midCurlStateHigh[j] = LowBits; - break; - } - - Transform(midCurlStateLow, midCurlStateHigh, - curlScratchpadLowStep1, curlScratchpadHighStep1); - } - - for (var i = 0; i < 162; i++) - switch (transactionTrits[offset++]) - { - case 0: - midCurlStateLow[i] = HighBits; - midCurlStateHigh[i] = HighBits; - break; - case 1: - midCurlStateLow[i] = LowBits; - midCurlStateHigh[i] = HighBits; - break; - default: - midCurlStateLow[i] = HighBits; - midCurlStateHigh[i] = LowBits; - break; - } - - unchecked - { - midCurlStateLow[162 + 0] = - (long) 0b1101101101101101101101101101101101101101101101101101101101101101L; - midCurlStateHigh[162 + 0] = - (long) 0b1011011011011011011011011011011011011011011011011011011011011011L; - midCurlStateLow[162 + 1] = - (long) 0b1111000111111000111111000111111000111111000111111000111111000111L; - midCurlStateHigh[162 + 1] = - (long) 0b1000111111000111111000111111000111111000111111000111111000111111L; - midCurlStateLow[162 + 2] = - 0b0111111111111111111000000000111111111111111111000000000111111111L; - midCurlStateHigh[162 + 2] = - (long) 0b1111111111000000000111111111111111111000000000111111111111111111L; - midCurlStateLow[162 + 3] = - (long) 0b1111111111000000000000000000000000000111111111111111111111111111L; - midCurlStateHigh[162 + 3] = - 0b0000000000111111111111111111111111111111111111111111111111111111L; - } - } - - // step2 - if (numberOfThreads <= 0) numberOfThreads = Math.Max(Environment.ProcessorCount - 1, 1); - - Parallel.For(0, numberOfThreads, threadIndex => - { - var midCurlStateCopyLow = new long[CurlStateLength]; - var midCurlStateCopyHigh = new long[CurlStateLength]; - Array.Copy(midCurlStateLow, 0, midCurlStateCopyLow, 0, CurlStateLength); - Array.Copy(midCurlStateHigh, 0, midCurlStateCopyHigh, 0, CurlStateLength); - for (var i = threadIndex; i > 0; i--) - Increment(midCurlStateCopyLow, midCurlStateCopyHigh, 162 + CurlHashLength / 9, - 162 + CurlHashLength / 9 * 2); - - var curlStateLow = new long[CurlStateLength]; - var curlStateHigh = new long[CurlStateLength]; - var curlScratchpadLowStep2 = new long[CurlStateLength]; - var curlScratchpadHighStep2 = new long[CurlStateLength]; - long outMask = 1; - - while (_state == State.Running) - { - Increment(midCurlStateCopyLow, midCurlStateCopyHigh, 162 + CurlHashLength / 9 * 2, - CurlHashLength); - - Array.Copy(midCurlStateCopyLow, 0, curlStateLow, 0, CurlStateLength); - Array.Copy(midCurlStateCopyHigh, 0, curlStateHigh, 0, CurlStateLength); - - Transform(curlStateLow, curlStateHigh, curlScratchpadLowStep2, curlScratchpadHighStep2); - - var mask = HighBits; - for (var i = minWeightMagnitude; i-- > 0;) - { - mask &= ~(curlStateLow[CurlHashLength - 1 - i] ^ curlStateHigh[CurlHashLength - 1 - i]); - if (mask == 0) break; - } - - if (mask == 0) continue; - - //sync - lock (SyncObj) - { - if (_state == State.Running) - { - _state = State.Completed; - while ((outMask & mask) == 0) outMask <<= 1; - - for (var i = 0; i < CurlHashLength; i++) - transactionTrits[TransactionLength - CurlHashLength + i] = - (midCurlStateCopyLow[i] & outMask) == 0 ? 1 - : (midCurlStateCopyHigh[i] & outMask) == 0 ? -1 : 0; - } - } - - break; - } - }); - - return _state == State.Completed; - } - - /// - /// - public void Cancel() - { - lock (SyncObj) - { - _state = State.Cancelled; - } - } - - private static void Transform(long[] curlStateLow, long[] curlStateHigh, - long[] curlScratchpadLow, long[] curlScratchpadHigh) - { - var curlScratchpadIndex = 0; - for (var round = 0; round < NumberOfRoundsp81; round++) - { - Array.Copy(curlStateLow, 0, curlScratchpadLow, 0, CurlStateLength); - Array.Copy(curlStateHigh, 0, curlScratchpadHigh, 0, CurlStateLength); - - for (var curlStateIndex = 0; curlStateIndex < CurlStateLength; curlStateIndex++) - { - var alpha = curlScratchpadLow[curlScratchpadIndex]; - var beta = curlScratchpadHigh[curlScratchpadIndex]; - if (curlScratchpadIndex < 365) - curlScratchpadIndex += 364; - else - curlScratchpadIndex -= 365; - - var gamma = curlScratchpadHigh[curlScratchpadIndex]; - var delta = (alpha | (~gamma)) & (curlScratchpadLow[curlScratchpadIndex] ^ beta); - - curlStateLow[curlStateIndex] = ~delta; - curlStateHigh[curlStateIndex] = (alpha ^ gamma) | delta; - } - } - } - - private static void Increment(long[] midCurlStateCopyLow, - long[] midCurlStateCopyHigh, int fromIndex, int toIndex) - { - for (var i = fromIndex; i < toIndex; i++) - if (midCurlStateCopyLow[i] == LowBits) - { - midCurlStateCopyLow[i] = HighBits; - midCurlStateCopyHigh[i] = LowBits; - } - else - { - if (midCurlStateCopyHigh[i] == LowBits) - midCurlStateCopyHigh[i] = HighBits; - else - midCurlStateCopyLow[i] = LowBits; - - break; - } - } - - private enum State - { - Running, - Cancelled, - Completed - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Pow/PearlDiverLocalPoW.cs b/IotaApi.Standard/Pow/PearlDiverLocalPoW.cs deleted file mode 100644 index 2d892a9..0000000 --- a/IotaApi.Standard/Pow/PearlDiverLocalPoW.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Iota.Api.Standard.Core; -using Iota.Api.Standard.Exception; -using Iota.Api.Standard.Utils; - -namespace Iota.Api.Standard.Pow -{ - /// - /// - public class PearlDiverLocalPoW : ILocalPoW - { - private readonly PearlDiver _pearlDiver = new PearlDiver(); - - /// - /// - /// - /// - /// - /// - public string PerformPoW(string trytes, int minWeightMagnitude) - { - var trits = Converter.ToTrits(trytes); - - if (!_pearlDiver.Search(trits, minWeightMagnitude, 0)) - throw new IllegalStateException("PearlDiver search failed"); - - return Converter.ToTrytes(trits); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/ArrayUtils.cs b/IotaApi.Standard/Utils/ArrayUtils.cs deleted file mode 100644 index 49f95b9..0000000 --- a/IotaApi.Standard/Utils/ArrayUtils.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Iota.Api.Standard.Utils -{ - internal class ArrayUtils - { - public static IEnumerable SliceRow(T[,] array, int row) - { - for (var i = array.GetLowerBound(1); i <= array.GetUpperBound(1); i++) - { - yield return array[row, i]; - } - } - - public static T[] SubArray(T[] data, int startIndex, int endIndex) - { - int length = endIndex - startIndex; - T[] result = new T[endIndex - startIndex]; - Array.Copy(data, startIndex, result, 0, length); - return result; - } - - public static T[] SubArray2(T[] data, int index, int length) - { - T[] result = new T[length]; - Array.Copy(data, index, result, 0, length); - return result; - } - } -} diff --git a/IotaApi.Standard/Utils/BigIntConverter.cs b/IotaApi.Standard/Utils/BigIntConverter.cs deleted file mode 100644 index 4fdd2d0..0000000 --- a/IotaApi.Standard/Utils/BigIntConverter.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using Org.BouncyCastle.Math; - -namespace Iota.Api.Standard.Utils -{ - /// - /// - /// - public class BigIntConverter - { - /// - /// - /// - /// - /// - /// - /// - public static BigInteger BigIntFromTrits(int[] trits, int offset, int size) - { - var value = BigInteger.Zero; - - for (var i = size; i-- > 0;) - value = value.Multiply(BigInteger.ValueOf(Converter.Radix)).Add(BigInteger.ValueOf(trits[offset + i])); - - return value; - } - - /// - /// - /// - /// - /// - /// - /// - public static BigInteger BigIntFromBytes(byte[] bytes, int offset, int size) - { - return new BigInteger(bytes, offset, size); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static void TritsFromBigInt(BigInteger value, int[] destination, int offset, int size) - { - if (destination.Length - offset < size) throw new ArgumentException("Destination array has invalid size"); - - var absoluteValue = value.CompareTo(BigInteger.Zero) < 0 ? value.Negate() : value; - for (var i = 0; i < size; i++) - { - var divRemainder = absoluteValue.DivideAndRemainder(BigInteger.ValueOf(Converter.Radix)); - var remainder = divRemainder[1].IntValue; - absoluteValue = divRemainder[0]; - - if (remainder > Converter.MaxTritValue) - { - remainder = Converter.MinTritValue; - absoluteValue = absoluteValue.Add(BigInteger.One); - } - - destination[offset + i] = remainder; - } - - if (value.CompareTo(BigInteger.Zero) < 0) - for (var i = 0; i < size; i++) - destination[offset + i] = -destination[offset + i]; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static void BytesFromBigInt(BigInteger value, byte[] destination, int offset,int size) - { - if (destination.Length - offset < size) - throw new ArgumentException("Destination array has invalid size."); - - var bytes = value.ToByteArray(); - var i = 0; - while (i + bytes.Length < size) destination[i++] = (byte) ((sbyte) bytes[0] < 0 ? -1 : 0); - - for (var j = bytes.Length; j-- > 0;) destination[i++] = bytes[bytes.Length - 1 - j]; - } - } -} diff --git a/IotaApi.Standard/Utils/Checksum.cs b/IotaApi.Standard/Utils/Checksum.cs deleted file mode 100644 index 1ac1cea..0000000 --- a/IotaApi.Standard/Utils/Checksum.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Iota.Api.Standard.Exception; -using Iota.Api.Standard.Pow; - -namespace Iota.Api.Standard.Utils -{ - /// - /// This class defines utility methods to add/remove the checksum to/from an address - /// - public static class Checksum - { - /// - /// Adds the checksum to the specified address - /// - /// An address without checksum - /// The address with the appended checksum - /// is thrown when an invalid address is provided - public static string AddChecksum(string address) - { - InputValidator.CheckAddress(address); - var addressWithChecksum = address; - addressWithChecksum += CalculateChecksum(address); - return addressWithChecksum; - } - - - /// - /// Removes the checksum from the specified address with checksum - /// - /// The address with checksum or without checksum. - /// the specified address without checksum - /// is thrown when the specified address is not an address with checksum - public static string RemoveChecksum(this string address) - { - if (IsAddressWithChecksum(address)) return GetAddress(address); - - if (IsAddressWithoutChecksum(address)) return address; - - throw new InvalidAddressException(address); - } - - - internal static string GetAddress(string addressWithChecksum) - { - return addressWithChecksum.Substring(0, Constants.AddressLengthWithoutChecksum); - } - - /// - /// Determines whether the specified address with checksum has a valid checksum. - /// - /// The address with checksum. - /// - /// true if the specified address with checksum has a valid checksum [the specified address with checksum]; - /// otherwise, false. - /// - public static bool IsValidChecksum(this string addressWithChecksum) - { - var addressWithoutChecksum = RemoveChecksum(addressWithChecksum); - var adressWithRecalculateChecksum = addressWithoutChecksum + CalculateChecksum(addressWithoutChecksum); - return adressWithRecalculateChecksum.Equals(addressWithChecksum); - } - - - private static bool IsAddressWithChecksum(string addressWithChecksum) - { - return InputValidator.IsAddress(addressWithChecksum) && - addressWithChecksum.Length == Constants.AddressLengthWithChecksum; - } - - private static bool IsAddressWithoutChecksum(string address) - { - return InputValidator.CheckAddress(address) && address.Length == Constants.AddressLengthWithoutChecksum; - } - - private static string CalculateChecksum(string address) - { - // TODO inject curl - ICurl curl = new Kerl(); - curl.Reset(); - curl.Absorb(Converter.ToTrits(address)); - var checksumTrits = new int[Sponge.HashLength]; - curl.Squeeze(checksumTrits); - var checksum = Converter.ToTrytes(checksumTrits); - return checksum.Substring(72, 9); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/Constants.cs b/IotaApi.Standard/Utils/Constants.cs deleted file mode 100644 index 76abcb0..0000000 --- a/IotaApi.Standard/Utils/Constants.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Iota.Api.Standard.Utils -{ - /// - /// This class defines different constants that are used accros the library - /// - public static class Constants - { - /// - /// This String contains all possible characters of the tryte alphabet - /// - public static readonly string TryteAlphabet = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - /// - /// The maximum seed length - /// - public static readonly int SeedLengthMax = 81; - - /// - /// This String represents the empty hash consisting of '9' - /// - public static readonly string EmptyHash = - "999999999999999999999999999999999999999999999999999999999999999999999999999999999"; - - /// - /// The length of an address without checksum - /// - public static readonly int AddressLengthWithoutChecksum = 81; - - /// - /// The address length with checksum - /// - public static readonly int AddressLengthWithChecksum = 90; - - /// - /// The length of an message - /// - public static int MessageLength = 2187; - /// - /// - /// - public static int TagLength = 27; - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/Converter.cs b/IotaApi.Standard/Utils/Converter.cs deleted file mode 100644 index 711dc66..0000000 --- a/IotaApi.Standard/Utils/Converter.cs +++ /dev/null @@ -1,378 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Iota.Api.Standard.Utils -{ - /// - /// This class provides a set of utility methods to are used to convert between different formats - /// - public class Converter - { - // ReSharper disable InconsistentNaming - /// - /// - /// - public static readonly uint HIGH_INTEGER_BITS = 0xFFFFFFFF; - - /// - /// - /// - public static readonly ulong HIGH_LONG_BITS = 0xFFFFFFFFFFFFFFFFL; - // ReSharper restore InconsistentNaming - - /// - /// The radix - /// - public static readonly int Radix = 3; - - /// - /// The maximum trit value - /// - public static readonly int MaxTritValue = (Radix - 1) / 2; - - /// - /// The minimum trit value - /// - public static readonly int MinTritValue = -MaxTritValue; - - /// - /// The number of trits in a byte - /// - public static readonly int NumberOfTritsInAByte = 5; - - /// - /// The number of trits in a tryte - /// - public static readonly int NumberOfTritsInATryte = 3; - - static readonly int[][] ByteToTritsMappings = new int[243][]; //why 243? max 121 - static readonly int[][] TryteToTritsMappings = new int[27][]; - - static Converter() - { - int[] trits = new int[NumberOfTritsInAByte]; - - for (int i = 0; i < 243; i++) - { - ByteToTritsMappings[i] = new int[NumberOfTritsInAByte]; - ByteToTritsMappings[i] = new int[NumberOfTritsInAByte]; - Array.Copy(trits, ByteToTritsMappings[i], NumberOfTritsInAByte); - Increment(trits, NumberOfTritsInAByte); - } - - for (int i = 0; i < 27; i++) - { - TryteToTritsMappings[i] = new int[NumberOfTritsInATryte]; - Array.Copy(trits, TryteToTritsMappings[i], NumberOfTritsInATryte); - Increment(trits, NumberOfTritsInATryte); - } - } - - /// - /// Converts the specified trits array to bytes - /// - /// The trits. - /// The offset to start from. - /// The size. - /// - public static byte[] ToBytes(int[] trits, int offset, int size) - { - byte[] bytes = new byte[(size + NumberOfTritsInAByte - 1) / NumberOfTritsInAByte]; - for (int i = 0; i < bytes.Length; i++) - { - int value = 0; - for ( - int j = (size - i * NumberOfTritsInAByte) < 5 - ? (size - i * NumberOfTritsInAByte) - : NumberOfTritsInAByte; - j-- > 0;) - { - value = value * Radix + trits[offset + i * NumberOfTritsInAByte + j]; - } - - bytes[i] = (byte) value; - } - - return bytes; - } - - /// - /// Converts the specified trits to trytes - /// - /// The trits. - /// - public static byte[] ToBytes(int[] trits) - { - return ToBytes(trits, 0, trits.Length); - } - - /// - /// Gets the trits from the specified bytes and stores it into the provided trits array - /// - /// The bytes. - /// The trits. - public static void GetTrits(sbyte[] bytes, int[] trits) - { - int offset = 0; - for (int i = 0; i < bytes.Length && offset < trits.Length; i++) - { - Array.Copy( - ByteToTritsMappings[bytes[i] < 0 ? (bytes[i] + ByteToTritsMappings.Length) : bytes[i]], 0, - trits, offset, - trits.Length - offset < NumberOfTritsInAByte - ? (trits.Length - offset) - : NumberOfTritsInAByte); - - offset += NumberOfTritsInAByte; - } - - while (offset < trits.Length) - { - trits[offset++] = 0; - } - } - - /// - /// Converts the specified trinary encoded string into a trits array of the specified length. - /// If the trytes string results in a shorter then specified trits array, then the remainder is padded we zeroes - /// - /// The trytes. - /// The length. - /// a trits array - public static int[] ToTrits(string trytes, int length) - { - int[] tritss = ToTrits(trytes); - - List tritsList = new List(); - - foreach (int i in tritss) - tritsList.Add(i); - - while (tritsList.Count < length) - tritsList.Add(0); - - return tritsList.ToArray(); - } - - - /// - /// Converts the specified trinary encoded trytes string to trits - /// - /// The trytes. - /// - public static int[] ToTrits(string trytes) - { - int[] d = new int[3 * trytes.Length]; - for (int i = 0; i < trytes.Length; i++) - { - Array.Copy(TryteToTritsMappings[Constants.TryteAlphabet.IndexOf(trytes[i])], 0, d, - i * NumberOfTritsInATryte, NumberOfTritsInATryte); - } - - return d; - } - - /// - /// - /// - /// - /// - /// - public static int[] ToTrits(long trytes, int length) - { - int[] tritss = ToTrits(trytes); - - List tritsList = new List(tritss); - - while (tritsList.Count < length) - tritsList.Add(0); - - return tritsList.ToArray(); - } - - /// - /// - /// - /// - /// - public static int[] ToTrits(long trytes) - { - if (trytes == 0) - return new[] {0}; - - var tritsList = new List(); - - while (trytes != 0) - { - var remainder = (int) (trytes % Radix); - trytes /= Radix; - - if (remainder > MaxTritValue) - { - remainder = MinTritValue; - trytes += 1; - } - else if (remainder < MinTritValue) - { - remainder = MaxTritValue; - trytes -= 1; - } - - tritsList.Add(remainder); - } - - return tritsList.ToArray(); - } - - /// - /// Copies the trits from the input string into the destination array - /// - /// The input string - /// The destination array. - /// - public static int[] CopyTrits(string input, int[] destination) - { - for (int i = 0; i < input.Length; i++) - { - int index = Constants.TryteAlphabet.IndexOf(input[i]); - destination[i * 3] = TryteToTritsMappings[index][0]; - destination[i * 3 + 1] = TryteToTritsMappings[index][1]; - destination[i * 3 + 2] = TryteToTritsMappings[index][2]; - } - - return destination; - } - - /// - /// Copies the trits in long representation into the destination array - /// - /// The value. - /// The destination array. - /// The offset from which copying is started. - /// The size. - public static void CopyTrits(long value, int[] destination, int offset, int size) - { - long absoluteValue = value < 0 ? -value : value; - for (int i = 0; i < size; i++) - { - int remainder = (int) (absoluteValue % Radix); - absoluteValue /= Radix; - if (remainder > MaxTritValue) - { - remainder = MinTritValue; - absoluteValue++; - } - - destination[offset + i] = remainder; - } - - if (value < 0) - { - for (int i = 0; i < size; i++) - { - destination[offset + i] = -destination[offset + i]; - } - } - } - - /// - /// Converts the trits array to a trytes string - /// - /// The trits. - /// The offset from which copying is started. - /// The size. - /// a trytes string - public static string ToTrytes(int[] trits, int offset, int size) - { - StringBuilder trytes = new StringBuilder(); - for (int i = 0; i < (size + NumberOfTritsInATryte - 1) / NumberOfTritsInATryte; i++) - { - int j = trits[offset + i * 3] + trits[offset + i * 3 + 1] * 3 + trits[offset + i * 3 + 2] * 9; - if (j < 0) - { - j += Constants.TryteAlphabet.Length; - } - - trytes.Append(Constants.TryteAlphabet[j]); - } - - return trytes.ToString(); - } - - /// - /// Converts the trits array to a trytes string - /// - /// The trits. - /// a trytes string - public static string ToTrytes(int[] trits) - { - return ToTrytes(trits, 0, trits.Length); - } - - /// - /// Converts the specified trits array to trytes in integer representation - /// - /// The trits. - /// The offset. - /// trytes in integer representation - public static int ToTryteValue(int[] trits, int offset) - { - return trits[offset] + trits[offset + 1] * 3 + trits[offset + 2] * 9; - } - - /// - /// Converts the specified trits to its corresponding integer value - /// - /// The trits. - /// an integer value representing the corresponding trits - public static int ToValue(int[] trits) - { - int value = 0; - - for (int i = trits.Length; i-- > 0;) - { - value = value * 3 + trits[i]; - } - - return value; - } - - /// - /// Converts the specified trits to its corresponding integer value - /// - /// The trits. - /// - public static long ToLongValue(int[] trits) - { - long value = 0; - - for (int i = trits.Length; i-- > 0;) - { - value = value * 3 + trits[i]; - } - - return value; - } - - /// - /// Increments the specified trits. - /// - /// The trits. - /// The size. - public static void Increment(int[] trits, int size) - { - for (int i = 0; i < size; i++) - { - if (++trits[i] > MaxTritValue) - { - trits[i] = MinTritValue; - } - else - { - break; - } - } - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/FixedBigIntConverter.cs b/IotaApi.Standard/Utils/FixedBigIntConverter.cs deleted file mode 100644 index 4c24a43..0000000 --- a/IotaApi.Standard/Utils/FixedBigIntConverter.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using Iesi.Collections.Generic; -using Iota.Api.Standard.Exception; - -namespace Iota.Api.Standard.Utils -{ - /// - /// - /// - public class FixedBigIntConverter - { - //243:3 384:2 - private const int TritsLength = 243; - - private const int BitLength = 384; - private const int ByteLength = BitLength / 8; - private const int IntLength = ByteLength / 4; - - /// hex representation of (3^242-1)/2 - private static readonly int[] Half3 = - { - unchecked((int) 0xa5ce8964), unchecked ((int) 0x9f007669), - 0x1484504f, 0x3ade00d9, - 0x0c24486e, 0x50979d57, - 0x79a4c702, 0x48bbae36, - unchecked ((int) 0xa9f6808b), unchecked ((int) 0xaa06a805), - unchecked ((int) 0xa87fabdf), 0x5e69ebef - }; - - /// - /// - /// - /// - /// - public static void FromTritsToBytes(int[] trits, byte[] bytes) - { - if (trits.Length != TritsLength) - throw new ArgumentException("trits array has invalid size"); - - if (bytes.Length != ByteLength) - throw new ArgumentException("bytes array has invalid size"); - - var baseHalf3 = new int[IntLength]; - - var setUniqueNumbers = new LinkedHashSet(); - foreach (var x in trits) setUniqueNumbers.Add(x); - - if (setUniqueNumbers.Count == 1 && setUniqueNumbers.Contains(-1)) - { - baseHalf3 = (int[]) Half3.Clone(); - BigIntNot(baseHalf3); - BigIntAdd(baseHalf3, 1); - } - else - { - var size = IntLength; - for (var i = TritsLength - 1; i-- > 0;) - { - { - // Multiply by radix - var sz = size; - var carry = 0; - - for (var j = 0; j < sz; j++) - { - // full_mul - var v = ToUnsignedLong(baseHalf3[j]) * 3 + - ToUnsignedLong(carry); - carry = (int) ((v >> (sizeof(int) * 8)) & 0xFFFFFFFF); - baseHalf3[j] = (int) (v & 0xFFFFFFFF); - } - - if (carry > 0) - { - baseHalf3[sz] = carry; - size += 1; - } - } - - var inValue = trits[i] + 1; - { - // Add - var sz = BigIntAdd(baseHalf3, inValue); - if (sz > size) size = sz; - } - } - - if (Sum(baseHalf3) != 0) - if (BigIntCmp(Half3, baseHalf3) <= 0) - { - // base is >= HALF_3. - // just do base - HALF_3 - baseHalf3 = BigIntSub(baseHalf3, Half3); - } - else - { - // we don't have a wrapping sub. - // so we need to be clever. - baseHalf3 = BigIntSub(Half3, baseHalf3); - BigIntNot(baseHalf3); - BigIntAdd(baseHalf3, 1); - } - } - - - // output - for (var i = 0; i < IntLength; i++) - { - bytes[i * 4 + 0] = (byte) ((baseHalf3[IntLength - 1 - i] & 0xFF000000) >> 24); - bytes[i * 4 + 1] = (byte) ((baseHalf3[IntLength - 1 - i] & 0x00FF0000) >> 16); - bytes[i * 4 + 2] = (byte) ((baseHalf3[IntLength - 1 - i] & 0x0000FF00) >> 8); - bytes[i * 4 + 3] = (byte) ((baseHalf3[IntLength - 1 - i] & 0x000000FF) >> 0); - } - } - - /// - /// - /// - /// - /// - public static void FromBytesToTrits(byte[] bytes, int[] trits) - { - if (bytes.Length != ByteLength) - throw new ArgumentException("bytes array has invalid size"); - - if (trits.Length != TritsLength) - throw new ArgumentException("trits array has invalid size"); - - var bigIntValue = new int[IntLength]; - - for (var i = 0; i < IntLength; i++) - { - bigIntValue[IntLength - 1 - i] = ToUnsignedInt(bytes[i * 4]) << 24; - bigIntValue[IntLength - 1 - i] |= ToUnsignedInt(bytes[i * 4 + 1]) << 16; - bigIntValue[IntLength - 1 - i] |= ToUnsignedInt(bytes[i * 4 + 2]) << 8; - bigIntValue[IntLength - 1 - i] |= ToUnsignedInt(bytes[i * 4 + 3]); - } - - if (BigIntCmp(bigIntValue, Half3) == 0) - { - var val = 0; - if (bigIntValue[0] > 0) - val = -1; - else if (bigIntValue[0] < 0) val = 1; - - for (var i = 0; i < TritsLength - 1; i++) trits[i] = val; - } - else - { - var flipTrits = false; - // See if we have a positive or negative two's complement number. - if (ToUnsignedLong(bigIntValue[IntLength - 1]) >> 31 != 0) - { - // negative value. - BigIntNot(bigIntValue); - if (BigIntCmp(bigIntValue, Half3) > 0) - { - bigIntValue = BigIntSub(bigIntValue, Half3); - flipTrits = true; - } - else - { - BigIntAdd(bigIntValue, 1); - bigIntValue = BigIntSub(Half3, bigIntValue); - } - } - else - { - // positive. we need to shift right by HALF_3 - bigIntValue = BigIntAdd(Half3, bigIntValue); - } - - var size = IntLength; - - for (var i = 0; i < TritsLength - 1; i++) - { - int remainder; - { - //div_rem - remainder = 0; - - for (var j = size - 1; j >= 0; j--) - { - var lhs = (ToUnsignedLong(remainder) << 32) | ToUnsignedLong(bigIntValue[j]); - var rhs = 3; - - var q = (int) (lhs / rhs); - var r = (int) (lhs % rhs); - bigIntValue[j] = q; - remainder = r; - } - } - trits[i] = remainder - 1; - } - - if (flipTrits) - for (var i = 0; i < TritsLength; i++) - trits[i] = -trits[i]; - } - } - - - #region Private Method - - private static int ToUnsignedInt(byte x) - { - return x & 0xff; - } - - private static long ToUnsignedLong(int i) - { - return i & 0xFFFFFFFFL; - } - - private static int Sum(int[] toSum) - { - var sum = 0; - foreach (var t in toSum) sum += t; - - return sum; - } - - private static void BigIntNot(int[] baseValue) - { - for (var i = 0; i < baseValue.Length; i++) baseValue[i] = ~baseValue[i]; - } - - private static int BigIntAdd(int[] baseValue, int rh) - { - var res = FullAdd(baseValue[0], rh, false); - baseValue[0] = res.Item1; - - var j = 1; - while (res.Item2) - { - res = FullAdd(baseValue[j], 0, true); - baseValue[j] = res.Item1; - j += 1; - } - - return j; - } - - private static int[] BigIntAdd(int[] lh, int[] rh) - { - var outValue = new int[IntLength]; - var carry = false; - for (var i = 0; i < IntLength; i++) - { - var ret = FullAdd(lh[i], rh[i], carry); - outValue[i] = ret.Item1; - carry = ret.Item2; - } - - if (carry) throw new IllegalStateException("Exceeded max value."); - - return outValue; - } - - private static int BigIntCmp(int[] lh, int[] rh) - { - for (var i = IntLength - 1; i >= 0; i--) - { - var a = ToUnsignedLong(lh[i]); - var b = ToUnsignedLong(rh[i]); - - var ret = a.CompareTo(b); - if (ret != 0) return ret; - } - - return 0; - } - - private static int[] BigIntSub(int[] lh, int[] rh) - { - var outValue = new int[IntLength]; - var noborrow = true; - - for (var i = 0; i < IntLength; i++) - { - var ret = FullAdd(lh[i], ~rh[i], noborrow); - outValue[i] = ret.Item1; - noborrow = ret.Item2; - } - - if (!noborrow) throw new IllegalStateException("noborrow"); - - return outValue; - } - - private static Tuple FullAdd(int ia, int ib, bool carry) - { - var a = ToUnsignedLong(ia); - var b = ToUnsignedLong(ib); - - var v = a + b; - var l = v >> 32; - var r = v & 0xFFFFFFFF; - var carry1 = l != 0; - - if (carry) v = r + 1; - - l = (v >> 32) & 0xFFFFFFFF; - r = v & 0xFFFFFFFF; - var carry2 = l != 0; - - return new Tuple((int) r, carry1 || carry2); - } - - #endregion - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/InputValidator.cs b/IotaApi.Standard/Utils/InputValidator.cs deleted file mode 100644 index 372f9f8..0000000 --- a/IotaApi.Standard/Utils/InputValidator.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Iota.Api.Standard.Exception; -using Iota.Api.Standard.Model; -using RestSharp.Extensions; - -namespace Iota.Api.Standard.Utils -{ - /// - /// This class provides methods to validate the parameters of different iota API methods - /// - public static class InputValidator - { - /// - /// Determines whether the specified string is an adrdress. - /// - /// The address. - /// - /// true if the specified string is an address; otherwise, false. - /// - public static bool IsAddress(string address) - { - if (address.Length == Constants.AddressLengthWithoutChecksum || - address.Length == Constants.AddressLengthWithChecksum) - { - return IsTrytes(address, address.Length); - } - return false; - } - - /// - /// - /// - /// - /// - public static bool IsHash(string hash) - { - return IsTrytes(hash, 81); - } - - /// - /// Checks whether the specified address is an address and throws and exception if the address is invalid - /// - /// address to check - /// exception which is thrown when the address is invalid - public static bool CheckAddress(string address) - { - if (!IsAddress(address)) - throw new InvalidAddressException(address); - - return true; - } - - /// - /// Determines whether the specified string represents an integer value. - /// - /// The value. - /// - /// true the specified string represents an integer value; otherwise, false. - /// - public static bool IsValue(string value) - { - // ReSharper disable once NotAccessedVariable - long tempValue; - return long.TryParse(value, out tempValue); - //return Regex.IsMatch(value, @"^(-){0,1}\d+$"); - } - - /// - /// Determines whether the specified array contains only valid hashes - /// - /// The hashes. - /// - /// true the specified array contains only valid hashes; otherwise, false. - /// - public static bool IsArrayOfHashes(string[] hashes) - { - if (hashes == null) - return false; - - foreach (string hash in hashes) - { - // Check if address with checksum - if (hash.Length == 90) - { - if (!IsTrytes(hash, 90)) - { - return false; - } - } - else - { - if (!IsTrytes(hash, 81)) - { - return false; - } - } - } - return true; - } - - /// - /// Determines whether the specified string contains only characters from the trytes alphabet (see ) - /// - /// The trytes. - /// The length. - /// - /// true if the specified trytes are trytes otherwise, false. - /// - public static bool IsTrytes(string trytes, int length) - { - string regex = "^[9A-Z]{" + (length == 0 ? "0," : length.ToString()) + "}$"; - var regexTrytes = new Regex(regex); - return regexTrytes.IsMatch(trytes); - } - - /// - /// Determines whether the specified string array contains only trytes - /// - /// The trytes. - /// The length. - /// - /// true if the specified array contains only valid trytes otherwise, false. - /// - public static bool IsArrayOfTrytes(string[] trytes, int length ) - { - return trytes.ToList().TrueForAll(element => IsTrytes(element, length)); - } - - /// - /// Determines whether the specified transfers are valid - /// - /// The transfers. - /// - /// true if the specified transfers are valid; otherwise, false. - /// - public static bool IsTransfersCollectionValid(ICollection transfers) - { - foreach (Transfer transfer in transfers) - { - if (!IsValidTransfer(transfer)) - { - return false; - } - } - return true; - } - - /// - /// Determines whether the specified transfer is valid. - /// - /// The transfer. - /// - /// true if the specified transfer is valid; otherwise, false. - /// - public static bool IsValidTransfer(Transfer transfer) - { - if (!IsAddress(transfer.Address)) - { - return false; - } - - // Check if message is correct trytes of any length - if (!IsTrytes(transfer.Message, 0)) - { - return false; - } - - // Check if tag is correct trytes of {0,27} trytes - return IsTrytes(transfer.Tag, 27); - } - - /// - /// Checks the specified specified transfers are valid. If not, an exception is thrown. - /// - /// The transactions array. - /// Not a transfer array - public static void CheckTransferArray(Transfer[] transactionsArray) - { - if (!IsTransfersCollectionValid(transactionsArray.ToList())) - throw new System.Exception("Not a transfer array"); - } - - /// - /// Checks if the seed is valid. If not, an exception is thrown. - /// - /// The seed. - /// - /// Invalid Seed: Format not in trytes - /// or - /// Invalid Seed: Seed too long - /// - public static void CheckIfValidSeed(string seed) - { - // validate the seed - if (!IsTrytes(seed, 0)) - { - throw new IllegalStateException("Invalid Seed: Format not in trytes"); - } - - // validate & if needed pad seed - if (seed.Length > 81) - { - throw new IllegalStateException("Invalid Seed: Seed too long"); - } - } - - /// - /// Pads the seed if necessary. - /// - /// The seed. - /// - public static string PadSeedIfNecessary(string seed) - { - while (seed.Length < Constants.AddressLengthWithoutChecksum) seed += 9; - return seed; - } - - /// - /// Checks if the specified array is an array of trytes. If not an exception is thrown. - /// - /// The trytes. - /// - public static void CheckIfArrayOfTrytes(string[] trytes) - { - if(!IsArrayOfTrytes(trytes, 2673)) - throw new InvalidTryteException(); - } - - /// - /// Determines whether the specified string consist only of '9'. - /// - /// The trytes. - /// The length. - /// - /// true if the specified string consist only of '9'; otherwise, false. - /// - public static bool IsNinesTrytes(string trytes, int length) - { - return trytes.Matches("^[9]{" + (length == 0 ? "0," : length.ToString()) + "}$"); - } - - /// - /// - /// - /// - /// - public static bool IsValidSeed(string seed) - { - return IsTrytes(seed, seed.Length); - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/IotaApiUtils.cs b/IotaApi.Standard/Utils/IotaApiUtils.cs deleted file mode 100644 index 15906ea..0000000 --- a/IotaApi.Standard/Utils/IotaApiUtils.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Pow; - -namespace Iota.Api.Standard.Utils -{ - /// - /// - /// - public class IotaApiUtils - { - /// - /// Generates a new address - /// - /// The tryte-encoded seed. It should be noted that this seed is not transferred. - /// The secuirty level of private key / seed. - /// The index to start search from. If the index is provided, the generation of the address is not deterministic. - /// The adds 9-tryte address checksum - /// The curl instance. - /// An String with address. - public static string NewAddress(string seed, int security, int index, bool checksum, ICurl curl) - { - if (security < 1) - throw new ArgumentException("invalid security level provided"); - - Signing signing = new Signing(curl); - int[] key = signing.Key(Converter.ToTrits(seed), index, security); - int[] digests = signing.Digests(key); - int[] addressTrits = signing.Address(digests); - - string address = Converter.ToTrytes(addressTrits); - - if (checksum) - address = Checksum.AddChecksum(address); - - return address; - } - - internal static List SignInputsAndReturn(string seed, - List inputs, - Bundle bundle, - List signatureFragments, ICurl curl) - { - bundle.FinalizeBundle(curl); - bundle.AddTrytes(signatureFragments); - - // SIGNING OF INPUTS - // - // Here we do the actual signing of the inputs - // Iterate over all bundle transactions, find the inputs - // Get the corresponding private key and calculate the signatureFragment - for (int i = 0; i < bundle.Transactions.Count; i++) - { - if (bundle.Transactions[i].Value < 0) - { - string thisAddress = bundle.Transactions[i].Address; - - // Get the corresponding keyIndex of the address - int keyIndex = 0; - foreach (Input input in inputs) - { - if (input.Address.Equals(thisAddress)) - { - keyIndex = input.KeyIndex; - break; - } - } - - string bundleHash = bundle.Transactions[i].Bundle; - - // Get corresponding private key of address - int[] key = new Signing(curl).Key(Converter.ToTrits(seed), keyIndex, 2); - - // First 6561 trits for the firstFragment - int[] firstFragment = ArrayUtils.SubArray2(key, 0, 6561); - - // Get the normalized bundle hash - int[] normalizedBundleHash = bundle.NormalizedBundle(bundleHash); - - // First bundle fragment uses 27 trytes - int[] firstBundleFragment = ArrayUtils.SubArray2(normalizedBundleHash, 0, 27); - - // Calculate the new signatureFragment with the first bundle fragment - int[] firstSignedFragment = new Signing(curl).SignatureFragment(firstBundleFragment, firstFragment); - - // Convert signature to trytes and assign the new signatureFragment - bundle.Transactions[i].SignatureMessageFragment = Converter.ToTrytes(firstSignedFragment); - - // Because the signature is > 2187 trytes, we need to - // find the second transaction to add the remainder of the signature - for (int j = 0; j < bundle.Transactions.Count; j++) - { - // Same address as well as value = 0 (as we already spent the input) - if (bundle.Transactions[j].Address.Equals(thisAddress) && - bundle.Transactions[j].Value == 0) - { - // Use the second 6562 trits - int[] secondFragment = ArrayUtils.SubArray2(key, 6561, 6561); - - // The second 27 to 54 trytes of the bundle hash - int[] secondBundleFragment = ArrayUtils.SubArray2(normalizedBundleHash, 27, 27); - - // Calculate the new signature - int[] secondSignedFragment = new Signing(curl).SignatureFragment(secondBundleFragment, - secondFragment); - - // Convert signature to trytes and assign it again to this bundle entry - bundle.Transactions[j].SignatureMessageFragment = (Converter.ToTrytes(secondSignedFragment)); - } - } - } - } - - List bundleTrytes = new List(); - - // Convert all bundle entries into trytes - foreach (Transaction tx in bundle.Transactions) - { - bundleTrytes.Add(tx.ToTransactionTrytes()); - } - - bundleTrytes.Reverse(); - return bundleTrytes; - } - - internal static long CreateTimeStampNow() - { - return (long) (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/IotaUnitConverter.cs b/IotaApi.Standard/Utils/IotaUnitConverter.cs deleted file mode 100644 index 893699a..0000000 --- a/IotaApi.Standard/Utils/IotaUnitConverter.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; - -namespace Iota.Api.Standard.Utils -{ - /// - /// This class provides methods to convert Iota to different units - /// - public class IotaUnitConverter - { - /// - /// Convert the iota amount - /// - /// amount - /// the source unit e.g. the unit of amount - /// the target unit - /// the specified amount in the target unit - public static double ConvertUnits(long amount, IotaUnits fromUnit, IotaUnits toUnit) - { - long amountInSource = (long) (amount*Math.Pow(10, (int) fromUnit)); - return ConvertUnits(amountInSource, toUnit); - } - - private static double ConvertUnits(long amount, IotaUnits toUnit) - { - int base10NormalizationExponent = (int) toUnit; - return (amount/Math.Pow(10, base10NormalizationExponent)); - } - - /// - /// Finds the optimal unit to display the specified amount in - /// - /// amount - /// the optimal IotaUnit - public static IotaUnits FindOptimalIotaUnitToDisplay(long amount) - { - int length = (amount).ToString().Length; - - if (amount < 0) - { - // do not count "-" sign - length -= 1; - } - - IotaUnits units = IotaUnits.Iota; - - if (length >= 1 && length <= 3) - { - units = IotaUnits.Iota; - } - else if (length > 3 && length <= 6) - { - units = IotaUnits.Kilo; - } - else if (length > 6 && length <= 9) - { - units = IotaUnits.Mega; - } - else if (length > 9 && length <= 12) - { - units = IotaUnits.Giga; - } - else if (length > 12 && length <= 15) - { - units = IotaUnits.Terra; - } - else if (length > 15 && length <= 18) - { - units = IotaUnits.Peta; - } - return units; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/IotaUnits.cs b/IotaApi.Standard/Utils/IotaUnits.cs deleted file mode 100644 index f3787ed..0000000 --- a/IotaApi.Standard/Utils/IotaUnits.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Iota.Api.Standard.Utils -{ - /// - /// Iota Units - /// - public enum IotaUnits - { - /// - /// The corresponding value is in iota. Same as 'None' () - /// - Iota = 0, - - /// - /// The corresponding value is in iota. Same as 'Iota' () - /// - None = 0, - - /// - /// 10^3 - /// - Kilo = 3, - - /// - /// 10^6 - /// - Mega = 6, - - /// - /// 10^9 - /// - Giga = 9, - - /// - /// 10^12 - /// - Terra = 12, - - /// - /// 10^15 - /// - Peta = 15, - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/Multisig.cs b/IotaApi.Standard/Utils/Multisig.cs deleted file mode 100644 index 8dcccbd..0000000 --- a/IotaApi.Standard/Utils/Multisig.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Pow; - -namespace Iota.Api.Standard.Utils -{ - /// - /// - public class Multisig - { - private readonly ICurl _curl; - private readonly Signing _signing; - - /// - /// - /// - public Multisig(ICurl curl) - { - _curl = curl; - _curl.Reset(); - _signing = new Signing(); - } - - /// - /// - public Multisig() : this(new Kerl()) - { - } - - /// - /// - /// Tryte-encoded seed. It should be noted that this seed is not transferred. - /// Secuirty level of private key / seed. - /// - /// Key index to start search from. If the index is provided, the generation of the address is not - /// deterministic. - /// - /// trytes - public string GetDigest(string seed, int security, int index) - { - var key = _signing.Key(Converter.ToTrits(seed, 243), index, security); - return Converter.ToTrytes(_signing.Digests(key)); - } - - /// - /// Gets the key value of a seed - /// - /// Tryte-encoded seed. It should be noted that this seed is not transferred - /// - /// Key index to start search from. If the index is provided, the generation of the address is not - /// deterministic. - /// - /// Secuirty level of private key / seed. - /// trytes - public string GetKey(string seed, int index, int security) - { - return Converter.ToTrytes( - _signing.Key(Converter.ToTrits(seed, 81 * security), - index, security)); - } - - /// - /// - /// digest trytes - public void AddAddressDigest(string[] digests) - { - foreach (var digest in digests) - { - // Get trits of digest - var digestTrits = Converter.ToTrits(digest); - - // Absorb digest - _curl.Absorb(digestTrits, 0, digestTrits.Length); - } - } - - /// - /// Generates a new address - /// - /// address - public string FinalizeAddress() - { - var addressTrits = new int[243]; - _curl.Squeeze(addressTrits); - - // Convert trits into trytes and return the address - return Converter.ToTrytes(addressTrits); - } - - /// - /// - /// - /// - /// - public bool ValidateAddress(string multisigAddress, int[][] digests) - { - // initialize Curl with the provided state - _curl.Reset(); - - foreach (var digest in digests) _curl.Absorb(digest); - - var addressTrits = new int[243]; - _curl.Squeeze(addressTrits); - - // Convert trits into trytes and return the address - return Converter.ToTrytes(addressTrits).Equals(multisigAddress); - } - - /// - /// - /// - /// - /// - /// - public Bundle AddSignature(Bundle bundleToSign, string inputAddress, string keyTrytes) - { - // Get the security used for the private key - // 1 security level = 2187 trytes - var security = keyTrytes.Length / Constants.MessageLength; - - // convert private key trytes into trits - var key = Converter.ToTrits(keyTrytes); - - - // First get the total number of already signed transactions - // use that for the bundle hash calculation as well as knowing - // where to add the signature - var numSignedTxs = 0; - - - for (var i = 0; i < bundleToSign.Transactions.Count; i++) - if (bundleToSign.Transactions[i].Address.Equals(inputAddress)) - if (!InputValidator.IsNinesTrytes(bundleToSign.Transactions[i].SignatureMessageFragment, - bundleToSign.Transactions[i].SignatureMessageFragment.Length)) - { - numSignedTxs++; - } - // Else sign the transactions - else - { - var bundleHash = bundleToSign.Transactions[i].Bundle; - - // First 6561 trits for the firstFragment - var firstFragment = new int[6561]; - Array.Copy(key, firstFragment, 6561); - - // Get the normalized bundle hash - var normalizedBundleFragments = new int[3][]; - for (var n = 0; n < 3; n++) normalizedBundleFragments[n] = new int[27]; - - var normalizedBundleHash = bundleToSign.NormalizedBundle(bundleHash); - - - // Split hash into 3 fragments - for (var k = 0; k < 3; k++) - Array.Copy(normalizedBundleHash, k * 27, normalizedBundleFragments[k], 0, 27); - - - // First bundle fragment uses 27 trytes - var firstBundleFragment = normalizedBundleFragments[numSignedTxs % 3]; - - // Calculate the new signatureFragment with the first bundle fragment - var firstSignedFragment = _signing.SignatureFragment(firstBundleFragment, firstFragment); - - // Convert signature to trytes and assign the new signatureFragment - bundleToSign.Transactions[i].SignatureMessageFragment = Converter.ToTrytes(firstSignedFragment); - - for (var j = 1; j < security; j++) - { - // Next 6561 trits for the firstFragment - var nextFragment = new int[6561]; - Array.Copy(key, 6561 * j, nextFragment, 0, 6561); - - // Use the next 27 trytes - var nextBundleFragment = normalizedBundleFragments[(numSignedTxs + j) % 3]; - - // Calculate the new signatureFragment with the first bundle fragment - var nextSignedFragment = _signing.SignatureFragment(nextBundleFragment, nextFragment); - - // Convert signature to trytes and add new bundle entry at i + j position - // Assign the signature fragment - bundleToSign.Transactions[i + j].SignatureMessageFragment = Converter.ToTrytes(nextSignedFragment); - } - - break; - } - - return bundleToSign; - } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/Rest/JsonSerializer.cs b/IotaApi.Standard/Utils/Rest/JsonSerializer.cs deleted file mode 100644 index 6fdf5c9..0000000 --- a/IotaApi.Standard/Utils/Rest/JsonSerializer.cs +++ /dev/null @@ -1,116 +0,0 @@ -#region License - -// Copyright 2010 John Sheehan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#endregion - -#region Acknowledgements - -// Original JsonSerializer contributed by Daniel Crenna (@dimebrain) - -#endregion - -using System; -using System.IO; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using RestSharp.Serializers; - -namespace Iota.Api.Standard.Utils.Rest -{ - /// - /// Default JSON serializer for request bodies - /// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes - /// - internal class JsonSerializer : ISerializer - { - private readonly Newtonsoft.Json.JsonSerializer _serializer; - - /// - /// Default serializer - /// - public JsonSerializer() - { - ContentType = "application/json"; - _serializer = new Newtonsoft.Json.JsonSerializer - { - MissingMemberHandling = MissingMemberHandling.Ignore, - NullValueHandling = NullValueHandling.Include, - DefaultValueHandling = DefaultValueHandling.Include - }; - } - - /// - /// Default serializer with overload for allowing custom Json.NET settings - /// - public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer) - { - ContentType = "application/json"; - _serializer = serializer; - } - - /// - /// Serialize the object as JSON - /// - /// Object to serialize - /// JSON as String - public string Serialize(object obj) - { - using (var stringWriter = new StringWriter()) - { - using (var jsonTextWriter = new JsonTextWriter(stringWriter)) - { - //jsonTextWriter.Formatting = Formatting.Indented; - //jsonTextWriter.QuoteChar = '"'; - _serializer.ContractResolver = new LowercaseContractResolver(); - - _serializer.Serialize(jsonTextWriter, obj); - - var result = stringWriter.ToString(); - return result; - } - } - } - - public class LowercaseContractResolver : DefaultContractResolver - { - protected override string ResolvePropertyName(string name) - { - // first letter small to match API naming conventions - return Char.ToLowerInvariant(name[0]) + name.Substring(1); - } - } - - /// - /// Unused for JSON Serialization - /// - public string DateFormat { get; set; } - - /// - /// Unused for JSON Serialization - /// - public string RootElement { get; set; } - - /// - /// Unused for JSON Serialization - /// - public string Namespace { get; set; } - - /// - /// Content type for serialized content - /// - public string ContentType { get; set; } - } -} \ No newline at end of file diff --git a/IotaApi.Standard/Utils/Rest/JsonWebClient.cs b/IotaApi.Standard/Utils/Rest/JsonWebClient.cs deleted file mode 100644 index bc011a8..0000000 --- a/IotaApi.Standard/Utils/Rest/JsonWebClient.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Net.Cache; -using System.Text; -using Iota.Api.Standard.Core; -using Iota.Api.Standard.Exception; -using Newtonsoft.Json; - -namespace Iota.Api.Standard.Utils.Rest -{ - internal class JsonWebClient - { - public TResponse GetPOSTResponseSync(Uri uri, string data) - { - Console.WriteLine("request: " + data); - - HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); - - request.ContentType = "application/json"; - request.Accept = "application/json"; - request.Method = "POST"; - request.AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate); - request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore); - request.Headers.Add("X-IOTA-API-Version", "1"); - request.Headers.Add("Origin", "iota.lib.csharp"); - request.Headers.Add("Accept-Language", "de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4 "); - request.KeepAlive = false; - request.Timeout = 300000; - request.ReadWriteTimeout = 300000; - - UTF8Encoding encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(data); - - request.ContentLength = bytes.Length; - - Stream requestStream = request.GetRequestStream(); - { - // Send the data. - requestStream.Write(bytes, 0, bytes.Length); - } - - try - { - using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) - { - using (Stream stream = response.GetResponseStream()) - { - StreamReader reader = new StreamReader(stream, Encoding.UTF8); - string responseString = reader.ReadToEnd(); - - if (response.StatusCode == HttpStatusCode.OK) - { - Console.WriteLine("response: " + responseString); - return JsonConvert.DeserializeObject(responseString); - } - - throw new IotaApiException(JsonConvert.DeserializeObject(responseString).Error); - } - } - } - catch (WebException ex) - { - Console.WriteLine("catched: " + ex + ex.Message); - - using (var stream = ex.Response.GetResponseStream()) - using (var reader = new StreamReader(stream)) - { - String errorResponse = reader.ReadToEnd(); - throw new IotaApiException(JsonConvert.DeserializeObject(errorResponse).Error); - } - } - } - - public void GetPOSTResponseAsync(Uri uri, string data, Action callback) - { - HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); - - request.Method = "POST"; - request.ContentType = "application-type/json;charset=utf-8"; - - UTF8Encoding encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(data); - - request.ContentLength = bytes.Length; - - using (Stream requestStream = request.GetRequestStream()) - { - // Send the data. - requestStream.Write(bytes, 0, bytes.Length); - } - - request.BeginGetResponse((x) => - { - using (HttpWebResponse response = (HttpWebResponse) request.EndGetResponse(x)) - { - if (callback != null) - { - using (Stream stream = response.GetResponseStream()) - { - StreamReader reader = new StreamReader(stream, Encoding.UTF8); - string responseString = reader.ReadToEnd(); - callback(JsonConvert.DeserializeObject(responseString)); - } - } - } - }, request); - } - } -} \ No newline at end of file diff --git a/IotaApi.sln b/IotaApi.sln deleted file mode 100644 index 0bccfcf..0000000 --- a/IotaApi.sln +++ /dev/null @@ -1,43 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2015 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IotaCSharpApi", "IotaCSharpApi\IotaCSharpApi.csproj", "{FC2C2F96-49EA-4046-95BD-3B570BDD1E13}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IotaCSharpApiUnitTests", "IotaCSharpApiUnitTests\IotaCSharpApiUnitTests.csproj", "{FAE71C0D-C373-4401-B9DE-BC3DC1D4E435}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IotaApi.Standard", "IotaApi.Standard\IotaApi.Standard.csproj", "{8E8D67DF-1A0E-45AA-8DE7-CBAD2EF4B900}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IotaApi.Standard.Tests", "IotaApi.Standard.Tests\IotaApi.Standard.Tests.csproj", "{1797EB77-756A-4B7C-94FD-FC86B7EB7930}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FC2C2F96-49EA-4046-95BD-3B570BDD1E13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC2C2F96-49EA-4046-95BD-3B570BDD1E13}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC2C2F96-49EA-4046-95BD-3B570BDD1E13}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC2C2F96-49EA-4046-95BD-3B570BDD1E13}.Release|Any CPU.Build.0 = Release|Any CPU - {FAE71C0D-C373-4401-B9DE-BC3DC1D4E435}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FAE71C0D-C373-4401-B9DE-BC3DC1D4E435}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FAE71C0D-C373-4401-B9DE-BC3DC1D4E435}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FAE71C0D-C373-4401-B9DE-BC3DC1D4E435}.Release|Any CPU.Build.0 = Release|Any CPU - {8E8D67DF-1A0E-45AA-8DE7-CBAD2EF4B900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8E8D67DF-1A0E-45AA-8DE7-CBAD2EF4B900}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8E8D67DF-1A0E-45AA-8DE7-CBAD2EF4B900}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8E8D67DF-1A0E-45AA-8DE7-CBAD2EF4B900}.Release|Any CPU.Build.0 = Release|Any CPU - {1797EB77-756A-4B7C-94FD-FC86B7EB7930}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1797EB77-756A-4B7C-94FD-FC86B7EB7930}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1797EB77-756A-4B7C-94FD-FC86B7EB7930}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1797EB77-756A-4B7C-94FD-FC86B7EB7930}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {953B5A71-F598-4C42-A718-80390ACB8FF8} - EndGlobalSection -EndGlobal diff --git a/IotaApi.sln.DotSettings b/IotaApi.sln.DotSettings deleted file mode 100644 index 6cfdce0..0000000 --- a/IotaApi.sln.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - POST \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/AddNeighborsRequest.cs b/IotaCSharpApi/Api/Core/AddNeighborsRequest.cs deleted file mode 100644 index 08e51bc..0000000 --- a/IotaCSharpApi/Api/Core/AddNeighborsRequest.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - - /// - /// This class represents the core API request 'AddNeighbors'. - /// It is used to add a neighbor to the node - /// - /// - public class AddNeighborsRequest : IotaRequest - { - /// - /// Gets or sets the uris. - /// - /// - /// The uris. - /// - public List Uris { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The uris of the neighbors to add. - public AddNeighborsRequest(List uris) : base(Core.Command.AddNeighbors.GetCommandString()) - { - Uris = uris; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Uris)}: {string.Join(",", Uris)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/AddNeighborsResponse.cs b/IotaCSharpApi/Api/Core/AddNeighborsResponse.cs deleted file mode 100644 index 1257e4f..0000000 --- a/IotaCSharpApi/Api/Core/AddNeighborsResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - - /// - /// Response of - /// - public class AddNeighborsResponse - { - /// - /// Gets the number of added neighbors. - /// - /// - /// The number of added neighbors. - /// - public long AddedNeighbors { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(AddedNeighbors)}: {AddedNeighbors}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/AttachToTangleRequest.cs b/IotaCSharpApi/Api/Core/AttachToTangleRequest.cs deleted file mode 100644 index d78fa74..0000000 --- a/IotaCSharpApi/Api/Core/AttachToTangleRequest.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - - /// - /// This class represents the core API request 'AttachToTangle'. - /// It is used to attach trytes to the tangle. - /// - public class AttachToTangleRequest : IotaRequest - { - private const int MinWeightMagnitudeMin = 18; - private int _minWeightMagnitude = MinWeightMagnitudeMin; - - /// - /// Initializes a new instance of the class. - /// - /// The trunk transaction. - /// The branch transaction. - /// The trytes. - /// The minimum weight magnitude. - public AttachToTangleRequest(string trunkTransaction, string branchTransaction, string[] trytes, - int minWeightMagnitude = 18) : base(Core.Command.AttachToTangle.GetCommandString()) - { - TrunkTransaction = trunkTransaction; - BranchTransaction = branchTransaction; - Trytes = trytes; - MinWeightMagnitude = minWeightMagnitude; - - if (Trytes == null) - Trytes = new string[0]; - } - - /// - /// Proof of Work intensity. Minimum value is 18 - /// - public int MinWeightMagnitude - { - get { return _minWeightMagnitude; } - set - { - if (value > MinWeightMagnitudeMin) - _minWeightMagnitude = value; - } - } - - /// - /// Trunk transaction to approve. - /// - public string TrunkTransaction { get; set; } - - /// - /// Branch transaction to approve. - /// - public string BranchTransaction { get; set; } - - /// - /// List of trytes (raw transaction data) to attach to the tangle. - /// - public string[] Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(MinWeightMagnitude)}: {MinWeightMagnitude}, {nameof(TrunkTransaction)}: {TrunkTransaction}, {nameof(BranchTransaction)}: {BranchTransaction}, {nameof(Trytes)}: {Trytes}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/AttachToTangleResponse.cs b/IotaCSharpApi/Api/Core/AttachToTangleResponse.cs deleted file mode 100644 index 6c91c0f..0000000 --- a/IotaCSharpApi/Api/Core/AttachToTangleResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Response of - /// - public class AttachToTangleResponse - { - /// - /// Gets or sets the trytes. - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/BroadcastTransactionsRequest.cs b/IotaCSharpApi/Api/Core/BroadcastTransactionsRequest.cs deleted file mode 100644 index 0a8b6ec..0000000 --- a/IotaCSharpApi/Api/Core/BroadcastTransactionsRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Broadcast a list of transactions to all neighbors. The input trytes for this call are provided by attachToTangle - /// - public class BroadcastTransactionsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The trytes. - public BroadcastTransactionsRequest(List trytes) - : base(Core.Command.BroadcastTransactions.GetCommandString()) - { - Trytes = trytes; - } - - /// - /// Gets or sets the trytes representing the transactions - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/BroadcastTransactionsResponse.cs b/IotaCSharpApi/Api/Core/BroadcastTransactionsResponse.cs deleted file mode 100644 index 36d4b39..0000000 --- a/IotaCSharpApi/Api/Core/BroadcastTransactionsResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Response of - /// - public class BroadcastTransactionsResponse - { - // empty - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/Command.cs b/IotaCSharpApi/Api/Core/Command.cs deleted file mode 100644 index 11df00f..0000000 --- a/IotaCSharpApi/Api/Core/Command.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.ComponentModel; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This enumeration defines the core API call commands - /// - public enum Command - { - /// - /// Adds neighbours to the node - /// - [Description("addNeighbors")] AddNeighbors, - - /// - /// Attaches to the tangle - /// - [Description("attachToTangle")] AttachToTangle, - - /// - /// Broadcasts transactions - /// - [Description("broadcastTransactions")] BroadcastTransactions, - - /// - /// Finds the transactions using different search criteria - /// - [Description("findTransactions")] FindTransactions, - - /// - /// Gets the balances - /// - [Description("getBalances")] GetBalances, - - /// - /// Gets the inclusion state - /// - [Description("getInclusionStates")] GetInclusionStates, - - /// - /// Gets the neighbours of the node - /// - [Description("getNeighbors")] GetNeighbors, - - /// - /// Get information about the node. - /// - [Description("getNodeInfo")] GetNodeInfo, - - /// - /// Gets the tips of the node - /// - [Description("getTips")] GetTips, - - /// - /// Gets the transactions to approve - /// - [Description("getTransactionsToApprove")] GetTransactionsToApprove, - - /// - /// Gets the trytes - /// - [Description("getTrytes")] GetTrytes, - - /// - /// Interrupt attaching to the tangle - /// - [Description("interruptAttachingToTangle")] InterruptAttachingToTangle, - - /// - /// Removes neighbours from the node - /// - [Description("removeNeighbors")] RemoveNeighbors, - - /// - /// Stores transactions - /// - [Description("storeTransactions")] StoreTransactions, - - /// - /// Get Missing Transactions - /// - [Description("getMissingTransactions")] GetMissingTransactions, - - /// - /// Check Consistency - /// - [Description("checkConsistency")] CheckConsistency, - - /// - /// Were Addresses SpentFrom - /// - [Description("wereAddressesSpentFrom")] WereAddressesSpentFrom, - - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/EnumHelper.cs b/IotaCSharpApi/Api/Core/EnumHelper.cs deleted file mode 100644 index c6967e7..0000000 --- a/IotaCSharpApi/Api/Core/EnumHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.ComponentModel; -using System.Reflection; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Helper class that extracts the command string corresponding to the different s - /// - public static class EnumHelper - { - /// - /// Retrieve the description on the enum - /// - /// The Enumeration - /// A string representing the friendly name - public static string GetCommandString(this Enum en) - { - Type type = en.GetType(); - - MemberInfo[] memInfo = type.GetMember(en.ToString()); - - if (memInfo.Length > 0) - { - object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); - - if (attrs.Length > 0) - { - return ((DescriptionAttribute) attrs[0]).Description; - } - } - - return en.ToString(); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/ErrorResponse.cs b/IotaCSharpApi/Api/Core/ErrorResponse.cs deleted file mode 100644 index c9382c1..0000000 --- a/IotaCSharpApi/Api/Core/ErrorResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - internal class ErrorResponse - { - public string Error { get; set; } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/FindTransactionsRequest.cs b/IotaCSharpApi/Api/Core/FindTransactionsRequest.cs deleted file mode 100644 index 8ddbf79..0000000 --- a/IotaCSharpApi/Api/Core/FindTransactionsRequest.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core api request 'FindTransactions' - /// - public class FindTransactionsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The bundles. - /// The addresses. - /// The tags. - /// The approvees. - public FindTransactionsRequest(List bundles, List addresses, List tags, - List approvees) : base(Core.Command.FindTransactions.GetCommandString()) - { - Bundles = bundles; - Addresses = addresses; - Tags = tags; - Approvees = approvees; - - if (Bundles == null) - Bundles = new List(); - if (Addresses == null) - Addresses = new List(); - if (Tags == null) - Tags = new List(); - if (Approvees == null) - Approvees = new List(); - } - - /// - /// Gets or sets the bundles. - /// - /// - /// The bundles. - /// - public List Bundles { get; set; } - - /// - /// Gets or sets the addresses. - /// - /// - /// The addresses. - /// - public List Addresses { get; set; } - - /// - /// Gets or sets the tags. - /// - /// - /// The tags. - /// - public List Tags { get; set; } - - /// - /// Gets or sets the approvees. - /// - /// - /// The approvees. - /// - public List Approvees { get; set; } - - /// - /// - /// - /// - public bool ShouldSerializeBundles() - { - return Bundles.Count > 0; - } - - /// - /// - /// - /// - public bool ShouldSerializeAddresses() - { - return Addresses.Count > 0; - } - - /// - /// - /// - /// - public bool ShouldSerializeTags() - { - return Tags.Count > 0; - } - - /// - /// - /// - /// - public bool ShouldSerializeApprovees() - { - return Approvees.Count > 0; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/FindTransactionsResponse.cs b/IotaCSharpApi/Api/Core/FindTransactionsResponse.cs deleted file mode 100644 index 0216f74..0000000 --- a/IotaCSharpApi/Api/Core/FindTransactionsResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Response of - /// - public class FindTransactionsResponse - { - /// - /// Gets or sets the hashes. - /// - /// - /// The hashes. - /// - public List Hashes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Hashes)}: {string.Join(",",Hashes)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GenericIotaCoreApi.cs b/IotaCSharpApi/Api/Core/GenericIotaCoreApi.cs deleted file mode 100644 index 8d0e718..0000000 --- a/IotaCSharpApi/Api/Core/GenericIotaCoreApi.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using Iota.Lib.CSharp.Api.Utils.Rest; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents a generic version of the core API that is used internally - /// - /// - public class GenericIotaCoreApi : IGenericIotaCoreApi - { - private readonly string _host; - private readonly int _port; - - /// - /// Initializes a new instance of the class. - /// - /// The host. - /// The port. - public GenericIotaCoreApi(string host, int port) - { - _host = host; - _port = port; - } - - /// - /// Gets the hostname. - /// - /// - /// The hostname. - /// - public string Hostname => _host; - - /// - /// Gets the port. - /// - /// - /// The port. - /// - public int Port => _port; - - /// - /// Requests the specified request. - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// - public TResponse Request(TRequest request) where TResponse : new() - { - JsonWebClient jsonWebClient = new JsonWebClient(); - return jsonWebClient.GetPOSTResponseSync(new Uri(CreateBaseUrl()), - new JsonSerializer().Serialize(request)); - } - - /// - /// Requests the specified request asynchronously - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// The response action. - public void RequestAsync(TRequest request, Action responseAction) - where TResponse : new() - { - JsonWebClient jsonWebClient = new JsonWebClient(); - jsonWebClient.GetPOSTResponseAsync(new Uri(CreateBaseUrl()), - new JsonSerializer().Serialize(request), responseAction); - } - - private string CreateBaseUrl() - { - return "http://" + _host + ":" + _port; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetBalancesRequest.cs b/IotaCSharpApi/Api/Core/GetBalancesRequest.cs deleted file mode 100644 index 3c15f0b..0000000 --- a/IotaCSharpApi/Api/Core/GetBalancesRequest.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core api request 'GetBalances' - /// - public class GetBalancesRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The addresses. - /// The threshold. - public GetBalancesRequest(List addresses, long threshold = 100) - : base(Core.Command.GetBalances.GetCommandString()) - { - Addresses = addresses; - Threshold = threshold; - } - - /// - /// Gets the threshold. - /// - /// - /// The threshold. - /// - public long Threshold { get; } - - /// - /// Gets the addresses. - /// - /// - /// The addresses. - /// - public List Addresses { get; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Threshold)}: {Threshold}, {nameof(Addresses)}: {string.Join(",",Addresses)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetBalancesResponse.cs b/IotaCSharpApi/Api/Core/GetBalancesResponse.cs deleted file mode 100644 index cb411e6..0000000 --- a/IotaCSharpApi/Api/Core/GetBalancesResponse.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Response of - /// - public class GetBalancesResponse : IotaResponse - { - /// - /// Gets or sets the balances. - /// - /// - /// The balances. - /// - public List Balances { get; set; } - - /// - /// Gets or sets the references. - /// - /// - /// The references. - /// - public List References { get; set; } - - /// - /// Gets or sets the index of the milestone. - /// - /// - /// The index of the milestone. - /// - public int MilestoneIndex { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return - $"{nameof(Balances)}: {string.Join(",", Balances)}, {nameof(References)}: {string.Join(",", References)}, {nameof(MilestoneIndex)}: {MilestoneIndex}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetInclusionStatesRequest.cs b/IotaCSharpApi/Api/Core/GetInclusionStatesRequest.cs deleted file mode 100644 index 09b100a..0000000 --- a/IotaCSharpApi/Api/Core/GetInclusionStatesRequest.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core API request 'GetInclusionStates' - /// - /// - public class GetInclusionStatesRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The transactions. - /// The tips. - public GetInclusionStatesRequest(string[] transactions, string[] tips) - : base(Core.Command.GetInclusionStates.GetCommandString()) - { - Transactions = transactions; - Tips = tips; - } - - /// - /// Gets the transactions. - /// - /// - /// The transactions. - /// - public string[] Transactions { get; } - - /// - /// Gets the tips. - /// - /// - /// The tips. - /// - public string[] Tips { get; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Transactions)}: {Transactions}, {nameof(Tips)}: {Tips}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetInclusionStatesResponse.cs b/IotaCSharpApi/Api/Core/GetInclusionStatesResponse.cs deleted file mode 100644 index f17b50c..0000000 --- a/IotaCSharpApi/Api/Core/GetInclusionStatesResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - /// - public class GetInclusionStatesResponse : IotaResponse - { - /// - /// Gets or sets the states. - /// - /// - /// The states. - /// - public List States { get; set; } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetNeighborsRequest.cs b/IotaCSharpApi/Api/Core/GetNeighborsRequest.cs deleted file mode 100644 index 2056c79..0000000 --- a/IotaCSharpApi/Api/Core/GetNeighborsRequest.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core API request 'GetNeighbors' - /// - /// - public class GetNeighborsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public GetNeighborsRequest() : base(Core.Command.GetNeighbors.GetCommandString()) - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetNeighborsResponse.cs b/IotaCSharpApi/Api/Core/GetNeighborsResponse.cs deleted file mode 100644 index 08448d4..0000000 --- a/IotaCSharpApi/Api/Core/GetNeighborsResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using Iota.Lib.CSharp.Api.Model; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Response of - /// - public class GetNeighborsResponse - { - /// - /// Gets or sets the neighbors. - /// - /// - /// The neighbors. - /// - public List Neighbors { get; set; } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetNodeInfoRequest.cs b/IotaCSharpApi/Api/Core/GetNodeInfoRequest.cs deleted file mode 100644 index 5445c81..0000000 --- a/IotaCSharpApi/Api/Core/GetNodeInfoRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// Returns information about your node - /// - public class GetNodeInfoRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public GetNodeInfoRequest() : base(Core.Command.GetNodeInfo.GetCommandString()) - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetNodeInfoResponse.cs b/IotaCSharpApi/Api/Core/GetNodeInfoResponse.cs deleted file mode 100644 index cad5fd8..0000000 --- a/IotaCSharpApi/Api/Core/GetNodeInfoResponse.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - /// - public class GetNodeInfoResponse : IotaResponse - { - /// - /// Name of the IOTA software you're currently using (IRI stands for Initial Reference Implementation). - /// - public string AppName { get; set; } - - /// - /// The version of the IOTA software you're currently running. - /// - public string AppVersion { get; set; } - - /// - /// Available cores on your machine for JRE. - /// - public int JreAvailableProcessors { get; set; } - - /// - /// The amount of free memory in the Java Virtual Machine. - /// - public long JreFreeMemory { get; set; } - - /// - /// The maximum amount of memory that the Java virtual machine will attempt to use. - /// - public long JreMaxMemory { get; set; } - - /// - /// The total amount of memory in the Java virtual machine. - /// - public long JreTotalMemory { get; set; } - - /// - /// Latest milestone that was signed off by the coordinator. - /// - public string LatestMilestone { get; set; } - - /// - /// Index of the latest milestone. - /// - public long LatestMilestoneIndex { get; set; } - - /// - /// The latest milestone which is solid and is used for sending transactions. - /// For a milestone to become solid your local node must basically approve the subtangle of coordinator-approved transactions, - /// and have a consistent view of all referenced transactions. - /// - public string LatestSolidSubtangleMilestone { get; set; } - - /// - /// Index of the latest solid subtangle. - /// - public long LatestSolidSubtangleMilestoneIndex { get; set; } - - /// - /// Number of neighbors you are directly connected with. - /// - public long Neighbors { get; set; } - - /// - /// Packets which are currently queued up - /// - public long PacketsQueueSize { get; set; } - - /// - /// Current UNIX timestamp. - /// - public long Time { get; set; } - - /// - /// Number of tips in the network. - /// - public long Tips { get; set; } - - /// - /// Transactions to request during syncing process. - /// - public long TransactionsToRequest { get; set; } - - /// - /// java runtime environment version. - /// - /// - /// The jre version. - /// - public string JreVersion { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return - $"{nameof(AppName)}: {AppName}, {nameof(AppVersion)}: {AppVersion}, {nameof(JreAvailableProcessors)}: {JreAvailableProcessors}, {nameof(JreFreeMemory)}: {JreFreeMemory}, {nameof(JreMaxMemory)}: {JreMaxMemory}, {nameof(JreTotalMemory)}: {JreTotalMemory}, {nameof(LatestMilestone)}: {LatestMilestone}, {nameof(LatestMilestoneIndex)}: {LatestMilestoneIndex}, {nameof(LatestSolidSubtangleMilestone)}: {LatestSolidSubtangleMilestone}, {nameof(LatestSolidSubtangleMilestoneIndex)}: {LatestSolidSubtangleMilestoneIndex}, {nameof(Neighbors)}: {Neighbors}, {nameof(PacketsQueueSize)}: {PacketsQueueSize}, {nameof(Time)}: {Time}, {nameof(Tips)}: {Tips}, {nameof(TransactionsToRequest)}: {TransactionsToRequest}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetTipsRequest.cs b/IotaCSharpApi/Api/Core/GetTipsRequest.cs deleted file mode 100644 index a08d6ee..0000000 --- a/IotaCSharpApi/Api/Core/GetTipsRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core API request 'GetTips' - /// - public class GetTipsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public GetTipsRequest() : base(Core.Command.GetTips.GetCommandString()) - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetTipsResponse.cs b/IotaCSharpApi/Api/Core/GetTipsResponse.cs deleted file mode 100644 index 094a7eb..0000000 --- a/IotaCSharpApi/Api/Core/GetTipsResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - public class GetTipsResponse : IotaResponse - { - /// - /// Gets or sets the hashes. - /// - /// - /// The hashes. - /// - public List Hashes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Hashes)}: {string.Join(",", Hashes)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetTransactionsToApproveRequest.cs b/IotaCSharpApi/Api/Core/GetTransactionsToApproveRequest.cs deleted file mode 100644 index 15dc874..0000000 --- a/IotaCSharpApi/Api/Core/GetTransactionsToApproveRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core API call 'GetTransactionsToApprove' - /// - public class GetTransactionsToApproveRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The depth. - public GetTransactionsToApproveRequest(int depth) - : base(Core.Command.GetTransactionsToApprove.GetCommandString()) - { - Depth = depth; - } - - /// - /// Gets the depth. - /// - /// - /// The depth. - /// - public int Depth { get; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Depth)}: {Depth}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetTransactionsToApproveResponse.cs b/IotaCSharpApi/Api/Core/GetTransactionsToApproveResponse.cs deleted file mode 100644 index 5bdca42..0000000 --- a/IotaCSharpApi/Api/Core/GetTransactionsToApproveResponse.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - public class GetTransactionsToApproveResponse : IotaResponse - { - /// - /// Gets or sets the trunk transaction. - /// - /// - /// The trunk transaction. - /// - public string TrunkTransaction { get; set; } - - /// - /// Gets or sets the branch transaction. - /// - /// - /// The branch transaction. - /// - public string BranchTransaction { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(TrunkTransaction)}: {TrunkTransaction}, {nameof(BranchTransaction)}: {BranchTransaction}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetTrytesRequest.cs b/IotaCSharpApi/Api/Core/GetTrytesRequest.cs deleted file mode 100644 index 31c13f0..0000000 --- a/IotaCSharpApi/Api/Core/GetTrytesRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core API request 'GetTrytes' - /// - public class GetTrytesRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public GetTrytesRequest() : base(Core.Command.GetTrytes.GetCommandString()) - { - - } - - /// - /// Gets or sets the hashes. - /// - /// - /// The hashes. - /// - public string[] Hashes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Hashes)}: {string.Join(",", Hashes)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/GetTrytesResponse.cs b/IotaCSharpApi/Api/Core/GetTrytesResponse.cs deleted file mode 100644 index 541f03b..0000000 --- a/IotaCSharpApi/Api/Core/GetTrytesResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - public class GetTrytesResponse - { - - /// - /// Gets or sets the trytes. - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/IGenericIotaCoreApi.cs b/IotaCSharpApi/Api/Core/IGenericIotaCoreApi.cs deleted file mode 100644 index 7cfde6a..0000000 --- a/IotaCSharpApi/Api/Core/IGenericIotaCoreApi.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This interface abstracts a generic version of the core api that is used internally. - /// - public interface IGenericIotaCoreApi - { - /// - /// Gets the hostname. - /// - /// - /// The hostname. - /// - string Hostname { get; } - - /// - /// Gets the port. - /// - /// - /// The port. - /// - int Port { get; } - - /// - /// Requests the specified request. - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// - TResponse Request(TRequest request) where TResponse : new(); - - /// - /// Requests the specified request asynchronously - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// The response action. - void RequestAsync(TRequest request, Action responseAction) - where TResponse : new(); - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/ILocalPoW.cs b/IotaCSharpApi/Api/Core/ILocalPoW.cs deleted file mode 100644 index b791dfe..0000000 --- a/IotaCSharpApi/Api/Core/ILocalPoW.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// - /// - public interface ILocalPoW - { - /// - /// - /// - /// - /// - /// - string PerformPoW(string trytes, int minWeightMagnitude); - } -} diff --git a/IotaCSharpApi/Api/Core/InterruptAttachingToTangleRequest.cs b/IotaCSharpApi/Api/Core/InterruptAttachingToTangleRequest.cs deleted file mode 100644 index cd204fc..0000000 --- a/IotaCSharpApi/Api/Core/InterruptAttachingToTangleRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core api request 'InterruptAttachingToTangle' - /// - public class InterruptAttachingToTangleRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - public InterruptAttachingToTangleRequest() : base(Core.Command.InterruptAttachingToTangle.GetCommandString()) - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/InterruptAttachingToTangleResponse.cs b/IotaCSharpApi/Api/Core/InterruptAttachingToTangleResponse.cs deleted file mode 100644 index ff466db..0000000 --- a/IotaCSharpApi/Api/Core/InterruptAttachingToTangleResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - /// - public class InterruptAttachingToTangleResponse : IotaResponse - { - - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/IotaCoreApi.cs b/IotaCSharpApi/Api/Core/IotaCoreApi.cs deleted file mode 100644 index 3808983..0000000 --- a/IotaCSharpApi/Api/Core/IotaCoreApi.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Utils; -using RestSharp.Extensions; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class provides access to the Iota core API - /// - public class IotaCoreApi - { - private readonly IGenericIotaCoreApi _genericIotaCoreApi; - - /// - /// - /// - public ILocalPoW LocalPow { get; set; } - - /// - /// Creates a core api object that uses the specified connection settings to connect to a node - /// - /// hostname or API address of a node to interact with - /// tcp/udp port - public IotaCoreApi(string host, int port) - { - _genericIotaCoreApi = new GenericIotaCoreApi(host, port); - } - - /// - /// Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work. - /// You need to supply branchTransaction as well as trunkTransaction - /// (basically the tips which you're going to validate and reference with this transaction) - /// - both of which you'll get through the getTransactionsToApprove API call. - /// - /// Trunk transaction to approve. - /// Branch transaction to approve. - /// List of trytes (raw transaction data) to attach to the tangle. - /// Proof of Work intensity. Minimum value is 18 - /// The returned value contains a different set of tryte values which you can input into broadcastTransactions and storeTransactions. - /// The returned tryte value, the last 243 trytes basically consist of the: trunkTransaction + branchTransaction + nonce. - /// These are valid trytes which are then accepted by the network. - public AttachToTangleResponse AttachToTangle(string trunkTransaction, string branchTransaction, - string[] trytes, int minWeightMagnitude = 18) - { - if (!InputValidator.IsHash(trunkTransaction)) - throw new ArgumentException("Invalid hashes provided."); - - if (!InputValidator.IsHash(branchTransaction)) - throw new ArgumentException("Invalid hashes provided."); - - if (!InputValidator.IsArrayOfTrytes(trytes, 2673)) - throw new ArgumentException("Invalid trytes provided."); - - if (LocalPow != null) - { - var response = new AttachToTangleResponse - { - Trytes = new List() - }; - - string previousTransaction = null; - foreach (var t in trytes) - { - var txn = new Transaction(t) - { - TrunkTransaction = previousTransaction ?? trunkTransaction, - BranchTransaction = previousTransaction == null ? branchTransaction : trunkTransaction - }; - - if (string.IsNullOrEmpty(txn.Tag) || txn.Tag.Matches("9*")) - txn.Tag = txn.ObsoleteTag; - txn.AttachmentTimestamp = IotaApiUtils.CreateTimeStampNow(); - txn.AttachmentTimestampLowerBound = 0; - txn.AttachmentTimestampUpperBound = 3_812_798_742_493L; - - var resultTrytes = LocalPow.PerformPoW(txn.ToTransactionTrytes(), minWeightMagnitude); - - previousTransaction = new Transaction(resultTrytes).Hash; - - response.Trytes.Add(resultTrytes); - } - - return response; - } - - AttachToTangleRequest attachToTangleRequest = new AttachToTangleRequest(trunkTransaction, branchTransaction, - trytes, minWeightMagnitude); - return _genericIotaCoreApi.Request(attachToTangleRequest); - } - - /// - /// Broadcasts the transactions. - /// - /// The transactions in trytes representation - /// the BroadcastTransactionsResponse - public BroadcastTransactionsResponse BroadcastTransactions(List trytes) - { - return - _genericIotaCoreApi.Request( - new BroadcastTransactionsRequest(trytes)); - } - - /// - /// Finds the transactions using the specified arguments as search criteria - /// - /// The addresses. - /// The tags. - /// The approves. - /// The bundles. - /// a FindTransactionsResponse, see - public FindTransactionsResponse FindTransactions(List addresses, List tags, - List approves, List bundles) - { - var findTransactionsRequest = new FindTransactionsRequest(bundles, addresses, tags, approves); - return - _genericIotaCoreApi.Request(findTransactionsRequest); - } - - /// - /// Gets the balances. - /// - /// The addresses. - /// The threshold. - /// It returns the confirmed balance which a list of addresses have at the latest confirmed milestone. - /// In addition to the balances, it also returns the milestone as well as the index with which the confirmed balance was determined. - /// The balances is returned as a list in the same order as the addresses were provided as input. - public GetBalancesResponse GetBalances(List addresses, long threshold) - { - List addressesWithoutChecksum = new List(); - foreach (var address in addresses) - { - string address0 = address.RemoveChecksum(); - addressesWithoutChecksum.Add(address0); - } - - GetBalancesRequest getBalancesRequest = new GetBalancesRequest(addressesWithoutChecksum, threshold); - return _genericIotaCoreApi.Request(getBalancesRequest); - } - - /// - /// Gets the inclusion states of the specified transactions - /// - /// The transactions. - /// The milestones. - /// a GetInclusionStatesResponse, see - public GetInclusionStatesResponse GetInclusionStates(string[] transactions, string[] milestones) - { - return - _genericIotaCoreApi.Request( - new GetInclusionStatesRequest(transactions, milestones)); - } - - /// - /// Stores the specified transactions in trytes into the local storage. The trytes to be used for this call are returned by attachToTangle. - /// - /// The trytes representing the transactions - /// a - public StoreTransactionsResponse StoreTransactions(List trytes) - { - return - _genericIotaCoreApi.Request( - new StoreTransactionsRequest(trytes)); - } - - /// - /// Gets the node information. - /// - /// a containing information about the node. - public GetNodeInfoResponse GetNodeInfo() - { - return _genericIotaCoreApi.Request(new GetNodeInfoRequest()); - } - - /// - /// Gets the tips. - /// - /// a containing a list of tips - public GetTipsResponse GetTips() - { - GetTipsRequest getTipsRequest = new GetTipsRequest(); - return _genericIotaCoreApi.Request(getTipsRequest); - } - - /// - /// Gets the transactions to approve. - /// - /// The depth is the number of bundles to go back to determine the transactions for approval. - /// The higher your depth value, the more "babysitting" you do for the network (as you have to confirm more transactions). - /// trunkTransaction and branchTransaction (result of the Tip selection) - public GetTransactionsToApproveResponse GetTransactionsToApprove(int depth) - { - GetTransactionsToApproveRequest getTransactionsToApproveRequest = new GetTransactionsToApproveRequest(depth); - return - _genericIotaCoreApi.Request( - getTransactionsToApproveRequest); - } - - /// - /// Gets the raw transaction data (trytes) of a specific transaction. - /// These trytes can then be easily converted into the actual transaction object using the constructor of Transaction - /// - /// The hashes of the transactions - /// a containing a list of trytes - public GetTrytesResponse GetTrytes(params string[] hashes) - { - GetTrytesRequest getTrytesRequest = new GetTrytesRequest() {Hashes = hashes}; - return _genericIotaCoreApi.Request(getTrytesRequest); - } - - /// - /// Interrupts and completely aborts the attachToTangle process. - /// - /// an - public InterruptAttachingToTangleResponse InterruptAttachingToTangle() - { - InterruptAttachingToTangleRequest request = new InterruptAttachingToTangleRequest(); - return - _genericIotaCoreApi.Request( - request); - } - - /// - /// Gets the neighbors the node is connected to - /// - /// A containing the set of neighbors the node is connected to as well as their activity count. The activity counter is reset after restarting IRI. - public GetNeighborsResponse GetNeighbors() - { - GetNeighborsRequest getNeighborsRequest = new GetNeighborsRequest(); - return _genericIotaCoreApi.Request(getNeighborsRequest); - } - - /// - /// Adds the neighbor(s) to the node. It should be noted that this is only temporary, and the added neighbors will be removed from your set of neighbors after you relaunch IRI. - /// - /// The uris of the neighbors to add. The URI (Unique Resource Identification) format is "udp://IPADDRESS:PORT" - /// containing the number of added Neighbors - public AddNeighborsResponse AddNeighbors(params string[] uris) - { - return - _genericIotaCoreApi.Request( - new AddNeighborsRequest(uris.ToList())); - } - - /// - /// Removes the neighbor(s) from the node. - /// - /// The uris of the neighbors to add. The URI (Unique Resource Identification) format is "udp://IPADDRESS:PORT" - /// A containing the number of removed neighbors - public RemoveNeighborsResponse RemoveNeighbors(params string[] uris) - { - RemoveNeighborsRequest removeNeighborsRequest = new RemoveNeighborsRequest(uris.ToList()); - return _genericIotaCoreApi.Request(removeNeighborsRequest); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/IotaRequest.cs b/IotaCSharpApi/Api/Core/IotaRequest.cs deleted file mode 100644 index 0c564ee..0000000 --- a/IotaCSharpApi/Api/Core/IotaRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class serves as base class for the different core API calls/requests - /// - public class IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The command. - public IotaRequest(string command) - { - this.Command = command; - } - - /// - /// Gets or sets the command. - /// - /// - /// The command. - /// - public string Command { get; set; } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/IotaResponse.cs b/IotaCSharpApi/Api/Core/IotaResponse.cs deleted file mode 100644 index 4735094..0000000 --- a/IotaCSharpApi/Api/Core/IotaResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the base class of different core API response classes - /// - public class IotaResponse - { - /// - /// Gets or sets the duration. - /// - /// - /// The duration. - /// - public long Duration { get; set; } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/RemoveNeighborsRequest.cs b/IotaCSharpApi/Api/Core/RemoveNeighborsRequest.cs deleted file mode 100644 index b448a9c..0000000 --- a/IotaCSharpApi/Api/Core/RemoveNeighborsRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core api request 'RemoveNeighbors' - /// - /// - public class RemoveNeighborsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The uris. - public RemoveNeighborsRequest(List uris) : base(Core.Command.RemoveNeighbors.GetCommandString()) - { - Uris = uris; - } - - /// - /// Gets or sets the uris of the neighbours to remove - /// - /// - /// The uris of the neighbours to remove. - /// - public List Uris { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Uris)}: {string.Join(",", Uris)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/RemoveNeighborsResponse.cs b/IotaCSharpApi/Api/Core/RemoveNeighborsResponse.cs deleted file mode 100644 index 2dded81..0000000 --- a/IotaCSharpApi/Api/Core/RemoveNeighborsResponse.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - public class RemoveNeighborsResponse - { - /// - /// Gets or sets the number of removed neighbors. - /// - /// - /// The removed neighbors. - /// - public long RemovedNeighbors { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(RemovedNeighbors)}: {RemovedNeighbors}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/StoreTransactionsRequest.cs b/IotaCSharpApi/Api/Core/StoreTransactionsRequest.cs deleted file mode 100644 index b015009..0000000 --- a/IotaCSharpApi/Api/Core/StoreTransactionsRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the core API request 'StoreTransactions'. - /// It stores transactions into the local storage. The trytes to be used for this call are returned by attachToTangle. - /// - public class StoreTransactionsRequest : IotaRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The trytes. - public StoreTransactionsRequest(List trytes) : base(Core.Command.StoreTransactions.GetCommandString()) - { - this.Trytes = trytes; - } - - /// - /// Gets or sets the trytes. - /// - /// - /// The trytes. - /// - public List Trytes { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Trytes)}: {Trytes}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Core/StoreTransactionsResponse.cs b/IotaCSharpApi/Api/Core/StoreTransactionsResponse.cs deleted file mode 100644 index 6a52d20..0000000 --- a/IotaCSharpApi/Api/Core/StoreTransactionsResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Core -{ - /// - /// This class represents the response of - /// - public class StoreTransactionsResponse - { - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/IllegalAccessError.cs b/IotaCSharpApi/Api/Exception/IllegalAccessError.cs deleted file mode 100644 index 03825d0..0000000 --- a/IotaCSharpApi/Api/Exception/IllegalAccessError.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception occurs when certain core API calls on the node are disabled - /// - /// - public class IllegalAccessError : System.Exception - { - - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/IllegalStateException.cs b/IotaCSharpApi/Api/Exception/IllegalStateException.cs deleted file mode 100644 index 4a547c1..0000000 --- a/IotaCSharpApi/Api/Exception/IllegalStateException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception occurs when an illegal state is encountered - /// - /// - public class IllegalStateException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The error. - public IllegalStateException(string error):base(error) - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/InvalidAddressException.cs b/IotaCSharpApi/Api/Exception/InvalidAddressException.cs deleted file mode 100644 index 83d2273..0000000 --- a/IotaCSharpApi/Api/Exception/InvalidAddressException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception occurs when an invalid address is provided - /// - /// - public class InvalidAddressException : ArgumentException - { - /// - /// Initializes a new instance of the class. - /// - /// The address. - public InvalidAddressException(string address) : base("The specified address '" + address + "' is invalid") - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/InvalidBundleException.cs b/IotaCSharpApi/Api/Exception/InvalidBundleException.cs deleted file mode 100644 index 1cc180a..0000000 --- a/IotaCSharpApi/Api/Exception/InvalidBundleException.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This excpetions occurs if an invalid bundle was found or provided - /// - /// - public class InvalidBundleException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public InvalidBundleException(string message) : base(message) - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/InvalidSignatureException.cs b/IotaCSharpApi/Api/Exception/InvalidSignatureException.cs deleted file mode 100644 index 6ddb479..0000000 --- a/IotaCSharpApi/Api/Exception/InvalidSignatureException.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception occurs when an invalid signature is encountered - /// - /// - public class InvalidSignatureException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - public InvalidSignatureException() :base("Invalid signature found") - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/InvalidTailTransactionException.cs b/IotaCSharpApi/Api/Exception/InvalidTailTransactionException.cs deleted file mode 100644 index 2084449..0000000 --- a/IotaCSharpApi/Api/Exception/InvalidTailTransactionException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception is thrown when an invalid tail transaction was encountered - /// - /// - public class InvalidTailTransactionException : System.Exception - { - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/InvalidTryteException.cs b/IotaCSharpApi/Api/Exception/InvalidTryteException.cs deleted file mode 100644 index 79a85ea..0000000 --- a/IotaCSharpApi/Api/Exception/InvalidTryteException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception occurs when invalid trytes are encountered - /// - public class InvalidTryteException : System.Exception - { - - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/InvisibleBundleTransactionException.cs b/IotaCSharpApi/Api/Exception/InvisibleBundleTransactionException.cs deleted file mode 100644 index 58f23c1..0000000 --- a/IotaCSharpApi/Api/Exception/InvisibleBundleTransactionException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception occurs when a bundle or transaction is not visible in the tangle - /// - /// - public class InvisibleBundleTransactionException : System.Exception - { - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/IotaApiException.cs b/IotaCSharpApi/Api/Exception/IotaApiException.cs deleted file mode 100644 index 1d52a4b..0000000 --- a/IotaCSharpApi/Api/Exception/IotaApiException.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception encapsulates an error that occured while communicating with the node (for example during a core API call) - /// - /// - public class IotaApiException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The error. - public IotaApiException(string error) : base(error) - { - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Exception/NotEnoughBalanceException.cs b/IotaCSharpApi/Api/Exception/NotEnoughBalanceException.cs deleted file mode 100644 index 3255cdc..0000000 --- a/IotaCSharpApi/Api/Exception/NotEnoughBalanceException.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Exception -{ - /// - /// This exception occurs when a transfer fails because their is not enough balance to perform the transfer - /// - /// - public class NotEnoughBalanceException : System.Exception - { - /// - /// Initializes a new instance of the class. - /// - public NotEnoughBalanceException() : base("Not enough balance") - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The total value. - public NotEnoughBalanceException(long totalValue) : base("Not enough balance to transfer " + totalValue + " iota") - { - - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/IotaApi.cs b/IotaCSharpApi/Api/IotaApi.cs deleted file mode 100644 index fe68696..0000000 --- a/IotaCSharpApi/Api/IotaApi.cs +++ /dev/null @@ -1,1124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Iota.Lib.CSharp.Api.Core; -using Iota.Lib.CSharp.Api.Exception; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Pow; -using Iota.Lib.CSharp.Api.Utils; - -namespace Iota.Lib.CSharp.Api -{ - /// - /// This class provides access to the core API methods and the proposed calls - /// - public class IotaApi : IotaCoreApi - { - private readonly ICurl _curl; - - /// - /// Creates an api object that uses the specified connection settings to connect to a node - /// - /// hostname or API address of a node to interact with - /// tcp/udp port - public IotaApi(string host, int port) : this(host, port, new Kerl()) - { - } - - /// - /// Creates an api object that uses the specified connection settings to connect to a node - /// - /// hostname or API address of a node to interact with - /// tcp/udp port - /// - /// a custom curl implementation to be used to perform the pow. Use the other constructor in order to - /// use the default curl implementation provided by the library - /// - public IotaApi(string host, int port, ICurl curl) : base(host, port) - { - _curl = curl ?? throw new ArgumentNullException(nameof(curl)); - } - - /// - /// Gets all possible inputs of a seed and returns them with the total balance. - /// This is either done deterministically (by genearating all addresses until findTransactions is empty and doing - /// getBalances), - /// or by providing a key range to use for searching through. - /// - /// Tryte-encoded seed. It should be noted that this seed is not transferred - /// The Security level of private key / seed. - /// Starting key index - /// Ending key index - /// The minimum threshold of accumulated balances from the inputs that is required - /// The inputs (see ) - public Inputs GetInputs(string seed, int security, int start, int end, long threshold) - { - InputValidator.CheckIfValidSeed(seed); - - seed = InputValidator.PadSeedIfNecessary(seed); - - if (security < 1) - throw new ArgumentException("invalid security level provided"); - - // If start value bigger than end, return error - if (start > end) - throw new ArgumentException("start must be smaller than end", nameof(start)); - - // or if difference between end and start is bigger than 500 keys - if (end - start > 500) - throw new ArgumentException("total number of keys exceeded 500"); - - // Case 1: start and end - // - // If start and end is defined by the user, simply iterate through the keys - // and call getBalances - if (end != 0) - { - var allAddresses = new string[end - start]; - - for (var i = start; i < end; i++) - { - var address = IotaApiUtils.NewAddress(seed, security, i, false, _curl); - allAddresses[i] = address; - } - - return GetBalanceAndFormat(allAddresses, threshold, start, security); - } - - // Case 2: iterate till threshold || end - // - // Either start from index: 0 or start (if defined) until threshold is reached. - // Calls getNewAddress and deterministically generates and returns all addresses - // We then do getBalance, format the output and return it - - var addresses = GetNewAddress(seed, security, start, false, 0, true); - return GetBalanceAndFormat(addresses, threshold, start, security); - } - - /// - /// Gets the balances of the specified addresses and calculates the total balance till the threshold is reached. - /// - /// addresses - /// the threshold - /// start index - /// - /// an Inputs object - /// - /// is thrown if threshold exceeds the sum of balance of the specified - /// addresses - /// - private Inputs GetBalanceAndFormat( - string[] addresses, - long threshold, int start, - int security) - { - if (security < 1) - throw new ArgumentException("invalid security level provided"); - - var getBalancesResponse = GetBalances(addresses.ToList(), 100); - - var balances = getBalancesResponse.Balances; - - var inputs = new Inputs {InputsList = new List(), TotalBalance = 0}; - - var threshholdReached = threshold == 0; - - for (var i = 0; i < addresses.Length; i++) - if (balances[i] > 0) - { - inputs.InputsList.Add(new Input - { - Address = addresses[i], - Balance = balances[i], - KeyIndex = start + i, - Security = security - }); - - inputs.TotalBalance += balances[i]; - - if (inputs.TotalBalance >= threshold) - { - threshholdReached = true; - break; - } - } - - if (threshholdReached) - return inputs; - - - throw new NotEnoughBalanceException(); - } - - /// - /// Main purpose of this function is to get an array of transfer objects as input, and then prepare the transfer by - /// generating the correct bundle, - /// as well as choosing and signing the inputs if necessary (if it's a value transfer). The output of this function is - /// an array of the raw transaction data (trytes) - /// - /// 81-tryte encoded address of recipient - /// - /// the transfers to prepare - /// Optional (default null). The inputs - /// - /// Optional (default null). if defined, this address will be used for sending the remainder - /// value (of the inputs) to. - /// - /// - /// a list containing the trytes of the new bundle - public List PrepareTransfers( - string seed, int security, - Transfer[] transfers, - string remainderAddress, - List inputs, - bool validateInputs) - { - // validate seed - if (!InputValidator.IsValidSeed(seed)) - throw new IllegalStateException("Invalid seed provided."); - - - if(security<1) - throw new ArgumentException("Invalid security level provided."); - - // Input validation of transfers object - InputValidator.CheckTransferArray(transfers); - - // Create a new bundle - var bundle = new Bundle(); - var signatureFragments = new List(); - - long totalValue = 0; - var tag = ""; - - // - // Iterate over all transfers, get totalValue - // and prepare the signatureFragments, message and tag - // - foreach (var transfer in transfers) - { - // remove the checksum of the address if provided - transfer.Address = transfer.Address.RemoveChecksum(); - - var signatureMessageLength = 1; - - // If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction) - if (transfer.Message.Length > Constants.MessageLength) - { - // Get total length, message / maxLength (2187 trytes) - signatureMessageLength += (int) Math.Floor((double) transfer.Message.Length / Constants.MessageLength); - - var msgCopy = transfer.Message; - - // While there is still a message, copy it - while (!string.IsNullOrEmpty(msgCopy)) - { - var fragment = msgCopy.Substring(0, 2187 > msgCopy.Length ? msgCopy.Length : 2187); - msgCopy = msgCopy.Substring(2187, msgCopy.Length - 2187); - - // Pad remainder of fragment - while (fragment.Length < 2187) fragment += '9'; - - signatureFragments.Add(fragment); - } - } - else - { - // Else, get single fragment with 2187 of 9's trytes - var fragment = string.Empty; - - if (!string.IsNullOrEmpty(transfer.Message)) - fragment = transfer.Message.Substring(0, - transfer.Message.Length < 2187 ? transfer.Message.Length : 2187); - - while (fragment.Length < 2187) fragment += '9'; - - signatureFragments.Add(fragment); - } - - // get current timestamp in seconds - var timestamp = (long)Math.Floor((double)IotaApiUtils.CreateTimeStampNow()/1000); - - // If no tag defined, get 27 tryte tag. - - tag = string.IsNullOrEmpty(transfer.Tag) ? "999999999999999999999999999" : transfer.Tag; - - - // Pad for required 27 tryte length - while (tag.Length < 27) tag += '9'; - - - // Add first entries to the bundle - // Slice the address in case the user provided a checksummed one - bundle.AddEntry(signatureMessageLength, transfer.Address, transfer.Value, tag, timestamp); - // Sum up total value - totalValue += transfer.Value; - } - - // Get inputs if we are sending tokens - if (totalValue != 0) - if (inputs != null && inputs.Count > 0) - { - // Get list if addresses of the provided inputs - var inputAddresses = new List(); - foreach (var input in inputs) inputAddresses.Add(input.Address); - - var balances = GetBalances(inputAddresses, 100); - - var confirmedInputs = new List(); - - long totalBalance = 0; - for (var i = 0; i < balances.Balances.Count; i++) - { - var thisBalance = balances.Balances[i]; - totalBalance += thisBalance; - - // If input has balance, add it to confirmedInputs - if (thisBalance > 0) - { - var inputEl = inputs[i]; - inputEl.Balance = thisBalance; - - confirmedInputs.Add(inputEl); - } - } - - // Return not enough balance error - if (totalValue > totalBalance) throw new NotEnoughBalanceException(totalValue); - - return AddRemainder(seed, security, confirmedInputs, bundle, tag, totalValue, remainderAddress, - signatureFragments); - } - - // Case 2: Get inputs deterministically - // - // If no inputs provided, derive the addresses from the seed and - // confirm that the inputs exceed the threshold - else - { - var inputList = GetInputs(seed, security, 0, 0, (int) totalValue).InputsList; - return AddRemainder(seed, security, inputList, bundle, tag, totalValue, remainderAddress, - signatureFragments); - } - - // If no input required, don't sign and simply finalize the bundle - bundle.FinalizeBundle(_curl.Clone()); - bundle.AddTrytes(signatureFragments); - - var bundleTrytes = new List(); - bundle.Transactions.ForEach(tx => bundleTrytes.Add(tx.ToTransactionTrytes())); - - bundleTrytes.Reverse(); - return bundleTrytes; - } - - - private List AddRemainder( - string seed, - int security, - List inputs, - Bundle bundle, - string tag, - long totalValue, - string remainderAddress, - List signatureFragments) - { - var totalTransferValue = totalValue; - - foreach (var input in inputs) - { - var thisBalance = input.Balance; - var toSubtract = 0 - thisBalance; - var timestamp = IotaApiUtils.CreateTimeStampNow(); - - // Add input as bundle entry - bundle.AddEntry(security, input.Address, toSubtract, tag, timestamp); - // If there is a remainder value - // Add extra output to send remaining funds to - - if (thisBalance >= totalTransferValue) - { - var remainder = thisBalance - totalTransferValue; - - // If user has provided remainder address - // Use it to send remaining funds to - if (remainder > 0 && remainderAddress != null) - { - // Remainder bundle entry - bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp); - - // function for signing inputs - IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, _curl); - } - else if (remainder > 0) - { - // Generate a new Address by calling getNewAddress - // ReSharper disable RedundantArgumentDefaultValue - var address = GetNewAddress(seed, security, 0, false, 0, false)[0]; - // ReSharper restore RedundantArgumentDefaultValue - - // Remainder bundle entry - bundle.AddEntry(1, address, remainder, tag, timestamp); - - // function for signing inputs - return IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, _curl); - } - else - { - // If there is no remainder, do not add transaction to bundle - // simply sign and return - return IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, _curl); - } - } - // If multiple inputs provided, subtract the totalTransferValue by - // the inputs balance - else - { - totalTransferValue -= thisBalance; - } - } - - throw new NotEnoughBalanceException(totalValue); - } - - - /// - /// Generates a new address from a seed and returns the remainderAddress. This is either done deterministically, or by - /// providing the index of the new remainderAddress - /// - /// Tryte-encoded seed. It should be noted that this seed is not transferred - /// - /// - /// Optional (default null). Key index to start search from. If the index is provided, the generation - /// of the address is not deterministic. - /// - /// Optional (default false). Adds 9-tryte address checksum - /// Optional (default 1)Total number of addresses to generate. - /// - /// If true, it returns all addresses which were deterministically generated (until - /// findTransactions returns null) - /// - /// an array of strings with the specifed number of addresses - public string[] GetNewAddress(string seed, int security, int index = 0, bool checksum = false, int total = 0, - bool returnAll = false) - { - var allAdresses = new List(); - - // TODO make two different functions out of this - - // Case 1: total - // - // If total number of addresses to generate is supplied, simply generate - // and return the list of all addresses - if (total > 0) - { - // Increase index with each iteration - for (var i = index; i < index + total; i++) - allAdresses.Add(IotaApiUtils.NewAddress(seed, security, i, checksum, new Kerl())); - - return allAdresses.ToArray(); - } - - // Case 2: no total provided - // - // Continue calling findTransactions to see if address was already created - // if null, return list of addresses - // - - var addresses = new List(); - - for (var i = index;; i++) - { - var newAddress = IotaApiUtils.NewAddress(seed, security, i, checksum, new Kerl()); - var response = FindTransactionsByAddresses(newAddress); - - if (returnAll) addresses.Add(newAddress); - - if (response.Hashes.Count == 0) - break; - } - - return addresses.ToArray(); - } - - /// - /// Gets the transfers which are associated with a seed. - /// The transfers are determined by either calculating deterministically which addresses were already used, - /// or by providing a list of indexes to get the transfers from. - /// - /// tryte-encoded seed. It should be noted that this seed is not transferred - /// If True, it gets the inclusion states of the transfers. - /// - /// the address start index - /// the address end index - /// An Array of Bundle object that represent the transfers - public Bundle[] GetTransfers(string seed, int security, int? start, int? end, bool inclusionStates = false) - { - InputValidator.CheckIfValidSeed(seed); - seed = InputValidator.PadSeedIfNecessary(seed); - - if (!start.HasValue) - start = 0; - if (!end.HasValue) - end = 0; - - // If start value bigger than end, return error - // or if difference between end and start is bigger than 500 keys - if (start.Value > end.Value || end.Value > start + 500) - throw new System.Exception("Invalid inputs provided: start, end"); - - // first call findTransactions - // If a transaction is non tail, get the tail transactions associated with it - // add it to the list of tail transactions - - var addresses = GetNewAddress(seed, security, start.Value, false, - end.Value, true); - - - var bundles = BundlesFromAddresses(addresses, inclusionStates); - return bundles; - } - - private Bundle[] BundlesFromAddresses(string[] addresses, bool inclusionStates) - { - var trxs = FindTransactionObjects(addresses); - // set of tail transactions - var tailTransactions = new List(); - var nonTailBundleHashes = new List(); - - foreach (var trx in trxs) - // Sort tail and nonTails - if (trx.CurrentIndex == 0) - { - tailTransactions.Add(trx.Hash); - } - else - { - if (nonTailBundleHashes.IndexOf(trx.Bundle) == -1) nonTailBundleHashes.Add(trx.Bundle); - } - - var bundleObjects = FindTransactionObjectsByBundle(nonTailBundleHashes.ToArray()); - foreach (var trx in bundleObjects) - // Sort tail and nonTails - if (trx.CurrentIndex == 0) - if (tailTransactions.IndexOf(trx.Hash) == -1) - tailTransactions.Add(trx.Hash); - - var finalBundles = new List(); - var tailTxArray = tailTransactions.ToArray(); - - // If inclusionStates, get the confirmation status - // of the tail transactions, and thus the bundles - GetInclusionStatesResponse gisr = null; - if (inclusionStates) - { - try - { - gisr = GetLatestInclusion(tailTxArray); - } - catch (IllegalAccessError) - { - // suppress exception (the check is done below) - } - - if (gisr == null || gisr.States == null || gisr.States.Count == 0) - throw new ArgumentException("Inclusion states not found"); - } - - - var finalInclusionStates = gisr; - - Parallel.ForEach(tailTransactions, param => - { - try - { - var b = GetBundle(param); - - if (inclusionStates) - { - var thisInclusion = finalInclusionStates != null && - finalInclusionStates.States[tailTxArray.ToList().IndexOf(param)]; - foreach (var t in b.Transactions) t.Persistance = thisInclusion; - } - - finalBundles.Add(b); - } - catch (System.Exception ex) - { - Console.WriteLine("Bundle error: " + ex.Message); - } - }); - - finalBundles.Sort(); - var returnValue = new Bundle[finalBundles.Count]; - for (var i = 0; i < finalBundles.Count; i++) - returnValue[i] = new Bundle(finalBundles[i].Transactions, finalBundles[i].Transactions.Count); - return returnValue; - } - - /// - /// Finds the transaction objects. - /// - /// The addresses. - /// a list of transactions - public List FindTransactionObjects(string[] addresses) - { - var addressesWithoutChecksum = - addresses.Select(address => address.RemoveChecksum()).ToList(); - - var ftr = FindTransactions(addressesWithoutChecksum, null, null, null); - if (ftr?.Hashes == null) - return null; - - // get the transaction objects of the transactions - return GetTransactionsObjects(ftr.Hashes.ToArray()); - } - - /// - /// Gets the transactions objects. - /// - /// The hashes in trytes - /// a list of transactions - public List GetTransactionsObjects(string[] hashes) - { - if (!InputValidator.IsArrayOfHashes(hashes)) - throw new IllegalStateException("Not an Array of Hashes: " + hashes); - - var trytesResponse = GetTrytes(hashes); - - var trxs = new List(); - - foreach (var tryte in trytesResponse.Trytes) trxs.Add(new Transaction(tryte, _curl)); - return trxs; - } - - /// - /// Finds the transaction objects by bundle. - /// - /// The bundles. - /// a list of Transaction objects - public List FindTransactionObjectsByBundle(string[] bundles) - { - var ftr = FindTransactions(null, null, null, bundles.ToList()); - if (ftr == null || ftr.Hashes == null) - return null; - - // get the transaction objects of the transactions - return GetTransactionsObjects(ftr.Hashes.ToArray()); - } - - - /// - /// Replays the bundle. - /// - /// The transaction. - /// The depth. - /// The minimum weight magnitude. - /// an array of boolean that indicate which transactions have been replayed successfully - public bool[] ReplayBundle(string transaction, int depth, int minWeightMagnitude) - { - //StopWatch stopWatch = new StopWatch(); - - var bundleTrytes = new List(); - - var bundle = GetBundle(transaction); - - bundle.Transactions.ForEach(t => bundleTrytes.Add(t.ToTransactionTrytes())); - - var trxs = SendTrytes(bundleTrytes.ToArray(), depth, minWeightMagnitude).ToList(); - - var successful = new bool[trxs.Count]; - - for (var i = 0; i < trxs.Count; i++) - { - var response = FindTransactionsByBundles(trxs[i].Bundle); - successful[i] = response.Hashes.Count != 0; - } - - return successful; - } - - /// - /// Finds the transactions by bundles. - /// - /// The bundles. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByBundles(params string[] bundles) - { - return FindTransactions(null, null, null, bundles.ToList()); - } - - /// - /// Finds the transactions by approvees. - /// - /// The approvees. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByApprovees(params string[] approvees) - { - return FindTransactions(null, null, approvees.ToList(), null); - } - - - /// - /// Finds the transactions by digests. - /// - /// The bundles. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByDigests(params string[] bundles) - { - return FindTransactions(null, bundles.ToList(), null, null); - } - - /// - /// Finds the transactions by addresses. - /// - /// The addresses. - /// a FindTransactionsResponse containing the transactions, see - public FindTransactionsResponse FindTransactionsByAddresses(params string[] addresses) - { - var addressesWithoutChecksum = new List(); - foreach (var address in addresses) - { - var address0 = address.RemoveChecksum(); - addressesWithoutChecksum.Add(address0); - } - - return FindTransactions(addressesWithoutChecksum, null, null, null); - } - - /// - /// Gets the latest inclusion. - /// - /// The hashes. - /// a GetInclusionStatesResponse cotaining the inclusion state of the specified hashes - public GetInclusionStatesResponse GetLatestInclusion(string[] hashes) - { - string[] latestMilestone = {GetNodeInfo().LatestSolidSubtangleMilestone}; - return GetInclusionStates(hashes, latestMilestone); - } - - - /// - /// Wrapper function that basically does prepareTransfers, as well as attachToTangle and finally, it broadcasts and - /// stores the transactions locally. - /// - /// tryte-encoded seed - /// - /// depth - /// The minimum weight magnitude - /// Array of transfer objects - /// Optional (default null). List of inputs used for funding the transfer - /// - /// Optional (default null). If defined, this address will be used for sending the remainder value - /// (of the inputs) to - /// - /// - /// - /// an array of the boolean that indicates which Transactions where sent successully - public bool[] SendTransfer( - string seed, int security, int depth, - int minWeightMagnitude, Transfer[] transfers, - Input[] inputs, string remainderAddress, - bool validateInputs, bool validateInputAddresses) - { - var trytes = PrepareTransfers(seed, security, transfers, - remainderAddress, inputs?.ToList(), validateInputs); - var trxs = SendTrytes(trytes.ToArray(), depth, minWeightMagnitude); - - var successful = new bool[trxs.Length]; - - for (var i = 0; i < trxs.Length; i++) - { - var response = FindTransactionsByBundles(trxs[i].Bundle); - - successful[i] = response.Hashes.Count != 0; - } - - return successful; - } - - /// - /// Sends the trytes. - /// - /// The trytes. - /// The depth. - /// Optional (default 14). The minimum weight magnitude. - /// an Array of Transactions - public Transaction[] SendTrytes(string[] trytes, int depth, int minWeightMagnitude = 14) - { - var transactionsToApproveResponse = GetTransactionsToApprove(depth); - - var attachToTangleResponse = - AttachToTangle(transactionsToApproveResponse.TrunkTransaction, - transactionsToApproveResponse.BranchTransaction, trytes, minWeightMagnitude); - try - { - BroadcastAndStore(attachToTangleResponse.Trytes); - } - catch (System.Exception) - { - return new Transaction[0]; - } - - var trx = new List(); - - foreach (var tx in attachToTangleResponse.Trytes) trx.Add(new Transaction(tx, _curl)); - return trx.ToArray(); - } - - /// - /// This function returns the bundle which is associated with a transaction. Input can by any type of transaction (tail - /// and non-tail). - /// If there are conflicting bundles (because of a replay for example) it will return multiple bundles. - /// It also does important validation checking (signatures, sum, order) to ensure that the correct bundle is returned. - /// - /// the transaction encoded in trytes - /// an array of bundle, if there are multiple arrays it means that there are conflicting bundles. - public Bundle GetBundle(string transaction) - { - if (!InputValidator.IsHash(transaction)) - { - throw new ArgumentException("Invalid hashes provided."); - } - - var bundle = TraverseBundle(transaction, null, new Bundle()); - - if (bundle == null) - throw new ArgumentException("Unknown Bundle"); - - long totalSum = 0; - var bundleHash = bundle.Transactions[0].Bundle; - - ICurl curl = new Kerl(); - curl.Reset(); - - var signaturesToValidate = new List(); - - for (var index = 0; index < bundle.Transactions.Count; index++) - { - var bundleTransaction = bundle.Transactions[index]; - var bundleValue = bundleTransaction.Value; - totalSum += bundleValue; - - if (bundleTransaction.CurrentIndex != index) - throw new InvalidBundleException("The index of the bundle " + bundleTransaction.CurrentIndex + - " did not match the expected index " + index); - - // Get the transaction trytes - var thisTxTrytes = bundleTransaction.ToTransactionTrytes().Substring(2187, 162); - - // Absorb bundle hash + value + timestamp + lastIndex + currentIndex trytes. - curl.Absorb(Converter.ToTrits(thisTxTrytes)); - - // Check if input transaction - if (bundleValue < 0) - { - var address = bundleTransaction.Address; - var sig = new Signature {Address = address}; - sig.SignatureFragments.Add(bundleTransaction.SignatureMessageFragment); - - // Find the subsequent txs with the remaining signature fragment - for (var i = index + 1; i < bundle.Transactions.Count; i++) - { - var newBundleTx = bundle.Transactions[i]; - - // Check if new tx is part of the signature fragment - if (newBundleTx.Address == address && newBundleTx.Value == 0) - { - if (sig.SignatureFragments.IndexOf(newBundleTx.SignatureMessageFragment) == -1) - sig.SignatureFragments.Add(newBundleTx.SignatureMessageFragment); - } - - } - - signaturesToValidate.Add(sig); - } - } - - // Check for total sum, if not equal 0 return error - if (totalSum != 0) - throw new InvalidBundleException("Invalid Bundle Sum"); - - var bundleFromTrxs = new int[243]; - curl.Squeeze(bundleFromTrxs); - var bundleFromTxString = Converter.ToTrytes(bundleFromTrxs); - - // Check if bundle hash is the same as returned by tx object - if (!bundleFromTxString.Equals(bundleHash)) - throw new InvalidBundleException("Invalid Bundle Hash"); - // Last tx in the bundle should have currentIndex === lastIndex - bundle.Length = bundle.Transactions.Count; - if ( - !bundle.Transactions[bundle.Length - 1].CurrentIndex.Equals( - bundle.Transactions[bundle.Length - 1].LastIndex)) - throw new InvalidBundleException("Invalid Bundle"); - - // Validate the signatures - foreach (var aSignaturesToValidate in signaturesToValidate) - { - var signatureFragments = aSignaturesToValidate.SignatureFragments.ToArray(); - var address = aSignaturesToValidate.Address; - var isValidSignature = new Signing().ValidateSignatures(address, signatureFragments, bundleHash); - - if (!isValidSignature) - throw new InvalidSignatureException(); - } - - return bundle; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public AccountData GetAccountData(String seed, int security, int index, bool checksum, int total, - bool returnAll, int start, int end, bool inclusionStates, long threshold) - { - - if (start > end || end > (start + 1000)) - { - throw new ArgumentException("Invalid input provided."); - } - - var addresses = GetNewAddress(seed, security, index, checksum, total, returnAll); - var bundle = GetTransfers(seed, security, start, end, inclusionStates); - var inputs = GetInputs(seed, security, start, end, threshold); - - return new AccountData(new List(addresses), bundle, inputs.InputsList, inputs.TotalBalance); - } - - /// - /// Wrapper function that broadcasts and stores the specified trytes - /// - /// trytes - public void BroadcastAndStore(List trytes) - { - BroadcastTransactions(trytes); - StoreTransactions(trytes); - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public List InitiateTransfer( - int securitySum, string inputAddress, string remainderAddress, - List transfers, bool testMode) - { - // validate input address - if (!InputValidator.IsAddress(inputAddress)) - throw new ArgumentException("Invalid addresses provided."); - - // validate remainder address - if (remainderAddress != null && !InputValidator.IsAddress(remainderAddress)) - { - throw new ArgumentException("Invalid addresses provided."); - } - - // Input validation of transfers object - if (!InputValidator.IsTransfersCollectionValid(transfers)) - { - throw new ArgumentException("Invalid transfers provided."); - } - - // Create a new bundle - Bundle bundle = new Bundle(); - - long totalValue = 0; - List signatureFragments = new List(); - String tag = ""; - // - - // Iterate over all transfers, get totalValue - // and prepare the signatureFragments, message and tag - foreach (Transfer transfer in transfers) - { - - // remove the checksum of the address if provided - if (transfer.Address.IsValidChecksum()) - { - transfer.Address = transfer.Address.RemoveChecksum(); - } - - int signatureMessageLength = 1; - - // If message longer than 2187 trytes, increase signatureMessageLength (add next transaction) - if (transfer.Message.Length > Constants.MessageLength) - { - - // Get total length, message / maxLength (2187 trytes) - signatureMessageLength += (int)Math.Floor((double)transfer.Message.Length / Constants.MessageLength); - - String msgCopy = transfer.Message; - - // While there is still a message, copy it - - while (!string.IsNullOrEmpty(msgCopy)) - { - - string fragment = msgCopy.Substring(0, Constants.MessageLength); - msgCopy = msgCopy.Substring(Constants.MessageLength, msgCopy.Length - Constants.MessageLength); - - // Pad remainder of fragment - fragment = fragment.PadRight(Constants.MessageLength, '9'); - - - signatureFragments.Add(fragment); - } - - } - else - { - - // Else, get single fragment with 2187 of 9's trytes - String fragment = transfer.Message; - - if (transfer.Message.Length < Constants.MessageLength) - { - fragment = fragment.PadRight(Constants.MessageLength, '9'); - } - - signatureFragments.Add(fragment); - - } - - tag = transfer.Tag; - - // pad for required 27 tryte length - if (transfer.Tag.Length < Constants.TagLength) - { - tag = tag.PadRight(Constants.TagLength, '9'); - } - - // get current timestamp in seconds - long timestamp = (long)Math.Floor(GetCurrentTimestampInSeconds()); - - // Add first entry to the bundle - bundle.AddEntry(signatureMessageLength, transfer.Address, transfer.Value, tag, timestamp); - // Sum up total value - totalValue += transfer.Value; - } - - // Get inputs if we are sending tokens - if (totalValue != 0) - { - GetBalancesResponse balancesResponse = GetBalances(new List { inputAddress }, 100); - var balances = balancesResponse.Balances; - - long totalBalance = 0; - - foreach (var balance in balances) - { - totalBalance += balance; - } - - // get current timestamp in seconds - long timestamp = (long)Math.Floor(GetCurrentTimestampInSeconds()); - - // bypass the balance checks during unit testing - if (testMode) - totalBalance += 1000; - - if (totalBalance > 0) - { - - long toSubtract = 0 - totalBalance; - - // Add input as bundle entry - // Only a single entry, signatures will be added later - bundle.AddEntry(securitySum, inputAddress, toSubtract, tag, timestamp); - } - // Return not enough balance error - if (totalValue > totalBalance) - { - throw new IllegalStateException("Not enough balance."); - } - - // If there is a remainder value - // Add extra output to send remaining funds to - if (totalBalance > totalValue) - { - - long remainder = totalBalance - totalValue; - - // Remainder bundle entry if necessary - if (remainderAddress == null) - { - throw new IllegalStateException("No remainder address defined."); - } - - bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp); - } - - bundle.FinalizeBundle(new Curl(CurlMode.CurlP81)); - bundle.AddTrytes(signatureFragments); - - return bundle.Transactions; - } - else - { - throw new System.Exception("Invalid value transfer: the transfer does not require a signature."); - } - } - - private Bundle TraverseBundle(string trunkTransaction, string bundleHash, Bundle bundle) - { - var gtr = GetTrytes(trunkTransaction); - - if (gtr.Trytes.Count == 0) - throw new InvisibleBundleTransactionException(); - - var trytes = gtr.Trytes[0]; - - var transaction = new Transaction(trytes, _curl); - - // If first transaction to search is not a tail, return error - if (bundleHash == null && transaction.CurrentIndex != 0) throw new InvalidTailTransactionException(); - - // If no bundle hash, define it - if (bundleHash == null) bundleHash = transaction.Bundle; - - // If different bundle hash, return with bundle - if (bundleHash != transaction.Bundle) return bundle; - - // If only one bundle element, return - if (transaction.LastIndex == 0 && transaction.CurrentIndex == 0) - return new Bundle(new List { transaction }, 1); - - // Define new trunkTransaction for search - var trunkTx = transaction.TrunkTransaction; - - // Add transaction object to bundle - bundle.Transactions.Add(transaction); - - // Continue traversing with new trunkTx - return TraverseBundle(trunkTx, bundleHash, bundle); - } - - private double GetCurrentTimestampInSeconds() - { - DateTime now = DateTime.UtcNow; - DateTime epoch = new DateTime - (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - return (now - epoch).TotalSeconds; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Model/AccountData.cs b/IotaCSharpApi/Api/Model/AccountData.cs deleted file mode 100644 index 2a7c296..0000000 --- a/IotaCSharpApi/Api/Model/AccountData.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// - /// - public class AccountData - { - /// - /// - /// - /// - /// - /// - /// - public AccountData(List addresses, - Bundle[] transferBundle, List inputList, long totalBalance) - { - Addresses = addresses; - TransferBundle = transferBundle; - InputList = inputList; - TotalBalance = totalBalance; - } - - /// - /// - /// - public List Addresses { get; set; } - - /// - /// - /// - public Bundle[] TransferBundle { get; set; } - - /// - /// - /// - public List InputList { get; set; } - - /// - /// - /// - public long TotalBalance { get; set; } - } -} diff --git a/IotaCSharpApi/Api/Model/Input.cs b/IotaCSharpApi/Api/Model/Input.cs deleted file mode 100644 index a73b95e..0000000 --- a/IotaCSharpApi/Api/Model/Input.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// This class represents an Input - /// - public class Input - { - /// - /// Gets or sets the address. - /// - /// - /// The address. - /// - public string Address { get; set; } - - /// - /// Gets or sets the balance. - /// - /// - /// The balance. - /// - public long Balance { get; set; } - - /// - /// Gets or sets the index of the key. - /// - /// - /// The index of the key. - /// - public int KeyIndex { get; set; } - - /// - /// Get or sets the security. - /// - /// - /// The security. - /// - public int Security { get; set; } - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Address)}: {Address}, {nameof(Balance)}: {Balance}, {nameof(KeyIndex)}: {KeyIndex}, {nameof(Security)}: {Security}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Model/Inputs.cs b/IotaCSharpApi/Api/Model/Inputs.cs deleted file mode 100644 index 52041a7..0000000 --- a/IotaCSharpApi/Api/Model/Inputs.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// This class represents the Inputs - /// - public class Inputs - { - /// - /// Gets or sets the inputs list. - /// - /// - /// The inputs list. - /// - public List InputsList { get; set; } - - /// - /// Gets or sets the total balance. - /// - /// - /// The total balance. - /// - public long TotalBalance { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return - $"Inputs:\n {string.Join(",", InputsList.Select(i => "[" + i + "]" + "\n"))}{nameof(TotalBalance)}: {TotalBalance}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Model/Neighbor.cs b/IotaCSharpApi/Api/Model/Neighbor.cs deleted file mode 100644 index 6fefe2f..0000000 --- a/IotaCSharpApi/Api/Model/Neighbor.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// This class represents a neigbhor - /// - public class Neighbor - { - /// - /// Gets or sets the address. - /// - /// - /// The address. - /// - public string Address { get; set; } - - /// - /// Gets or sets the number of all transactions. - /// - /// - /// The number of all transactions. - /// - public long NumberOfAllTransactions { get; set; } - - /// - /// Gets or sets the number of invalid transactions. - /// - /// - /// The number of invalid transactions. - /// - public long NumberOfInvalidTransactions { get; set; } - - /// - /// Gets or sets the number of new transactions. - /// - /// - /// The number of new transactions. - /// - public long NumberOfNewTransactions { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Address)}: {Address}, {nameof(NumberOfAllTransactions)}: {NumberOfAllTransactions}, {nameof(NumberOfInvalidTransactions)}: {NumberOfInvalidTransactions}, {nameof(NumberOfNewTransactions)}: {NumberOfNewTransactions}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Model/Signature.cs b/IotaCSharpApi/Api/Model/Signature.cs deleted file mode 100644 index 75dbdec..0000000 --- a/IotaCSharpApi/Api/Model/Signature.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// Thic class represents a signature - /// - public class Signature - { - /// - /// Initializes a new instance of the class. - /// - public Signature() - { - SignatureFragments = new List(); - } - - /// - /// Gets or sets the address. - /// - /// - /// The address. - /// - public string Address { get; set; } - - /// - /// Gets or sets the signature fragments. - /// - /// - /// The signature fragments. - /// - public List SignatureFragments { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Address)}: {Address}, {nameof(SignatureFragments)}: {string.Join(",", SignatureFragments)}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Model/Transaction.cs b/IotaCSharpApi/Api/Model/Transaction.cs deleted file mode 100644 index 761ca29..0000000 --- a/IotaCSharpApi/Api/Model/Transaction.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using Iota.Lib.CSharp.Api.Pow; -using Iota.Lib.CSharp.Api.Utils; - -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// This class represents an iota transaction - /// - public class Transaction - { - /// - /// Initializes a new instance of the class. - /// - public Transaction() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The trytes representing the transaction - /// The curl implementation. - /// - /// trytes must non-null - /// or - /// position " + i + "must not be '9' - /// - public Transaction(string trytes, ICurl curl) - { - if (string.IsNullOrEmpty(trytes)) throw new ArgumentException("trytes must non-null"); - - // validity check - for (var i = 2279; i < 2295; i++) - if (trytes[i] != '9') - throw new ArgumentException("position " + i + "must not be '9'"); - - var transactionTrits = Converter.ToTrits(trytes); - var hash = new int[243]; - - // generate the correct transaction hash - curl.Reset(); - curl.Absorb(transactionTrits, 0, transactionTrits.Length); - curl.Squeeze(hash, 0, hash.Length); - - Hash = Converter.ToTrytes(hash); - SignatureMessageFragment = trytes.Substring(0, 2187); - Address = trytes.Substring(2187, 2268 - 2187); - Value = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6804, 6837)); - ObsoleteTag = trytes.Substring(2295, 2322 - 2295); - Timestamp = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6966, 6993)); - CurrentIndex = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6993, 7020)); - LastIndex = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7020, 7047)); - Bundle = trytes.Substring(2349, 2430 - 2349); - TrunkTransaction = trytes.Substring(2430, 2511 - 2430); - BranchTransaction = trytes.Substring(2511, 2592 - 2511); - Tag = trytes.Substring(2592, 2619 - 2592); - AttachmentTimestamp = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7857, 7884)); - AttachmentTimestampLowerBound = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7884, 7911)); - AttachmentTimestampUpperBound = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7911, 7938)); - Nonce = trytes.Substring(2646, 2673 - 2646); - } - - /// - /// Initializes a new instance of the class. - /// - /// The trytes representing the transaction - public Transaction(string trytes) : this(trytes, new Curl(CurlMode.CurlP81)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The address. - /// The value. - /// The tag. - /// The timestamp. - public Transaction(string address, long value, string tag, long timestamp) - { - Address = address; - Value = value; - Tag = tag; - ObsoleteTag = tag; - Timestamp = timestamp; - } - - /// - /// Gets or sets the hash. - /// - /// - /// The hash. - /// - public string Hash { get; set; } - - /// - /// Gets or sets the signature fragment. - /// - /// - /// The signature fragment. - /// - public string SignatureMessageFragment { get; set; } - - /// - /// Gets or sets the address. - /// - /// - /// The address. - /// - public string Address { get; set; } - - /// - /// Gets or sets the value. - /// - /// - /// The value. - /// - public long Value { get; set; } - - /// - /// Gets or sets the tag. - /// - /// - /// The tag. - /// - public string ObsoleteTag { get; set; } - - /// - /// Gets or sets the timestamp. - /// - /// - /// The timestamp. - /// - public long Timestamp { get; set; } - - /// - /// Gets or sets the index of the current. - /// - /// - /// The index of the current. - /// - public long CurrentIndex { get; set; } - - /// - /// Gets or sets the last index. - /// - /// - /// The last index. - /// - public long LastIndex { get; set; } - - /// - /// Gets or sets the bundle. - /// - /// - /// The bundle. - /// - public string Bundle { get; set; } - - /// - /// Gets or sets the trunk transaction. - /// - /// - /// The trunk transaction. - /// - public string TrunkTransaction { get; set; } - - /// - /// Gets or sets the branch transaction. - /// - /// - /// The branch transaction. - /// - public string BranchTransaction { get; set; } - - /// - /// - public string Tag { get; set; } - - /// - /// - public long AttachmentTimestamp { get; set; } - - /// - /// - public long AttachmentTimestampLowerBound { get; set; } - - /// - /// - public long AttachmentTimestampUpperBound { get; set; } - - /// - /// Gets or sets the nonce. - /// - /// - /// The nonce. - /// - public string Nonce { get; set; } - - - /// - /// Gets or sets a value indicating whether this is persistance. - /// - /// - /// true if persistance; otherwise, false. - /// - public bool Persistance { get; set; } - - /// - /// Converts the transaction to the corresponding trytes representation - /// - /// - public string ToTransactionTrytes() - { - var valueTrits = Converter.ToTrits(Value, 81); - var timestampTrits = Converter.ToTrits(Timestamp, 27); - var currentIndexTrits = Converter.ToTrits(CurrentIndex, 27); - var lastIndexTrits = Converter.ToTrits(LastIndex, 27); - var attachmentTimestampTrits = Converter.ToTrits(AttachmentTimestamp, 27); - var attachmentTimestampLowerBoundTrits = Converter.ToTrits(AttachmentTimestampLowerBound, 27); - var attachmentTimestampUpperBoundTrits = Converter.ToTrits(AttachmentTimestampUpperBound, 27); - - if (string.IsNullOrEmpty(Tag)) - Tag = ObsoleteTag; - - return SignatureMessageFragment - + Address - + Converter.ToTrytes(valueTrits) - + ObsoleteTag - + Converter.ToTrytes(timestampTrits) - + Converter.ToTrytes(currentIndexTrits) - + Converter.ToTrytes(lastIndexTrits) - + Bundle - + TrunkTransaction - + BranchTransaction - + Tag - + Converter.ToTrytes(attachmentTimestampTrits) - + Converter.ToTrytes(attachmentTimestampLowerBoundTrits) - + Converter.ToTrytes(attachmentTimestampUpperBoundTrits) - + Nonce; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return - $@"{nameof(Value)}: {Value}, -{nameof(Persistance)}: {Value}, -{nameof(Tag)}: {Tag}, -{nameof(Hash)}: {Hash}, -{nameof(SignatureMessageFragment)}: {SignatureMessageFragment}, -{nameof(Address)}: {Address}, -{nameof(Timestamp)}: {Timestamp}, -{nameof(Bundle)}: {Bundle}, -{nameof(TrunkTransaction)}: {TrunkTransaction}, -{nameof(BranchTransaction)}: {BranchTransaction}, -{nameof(LastIndex)}: {LastIndex}, -{nameof(CurrentIndex)}: {CurrentIndex}, -{nameof(Nonce)}: {Nonce}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Model/Transfer.cs b/IotaCSharpApi/Api/Model/Transfer.cs deleted file mode 100644 index 46bb1d3..0000000 --- a/IotaCSharpApi/Api/Model/Transfer.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// This class represents a Transfer - /// - public class Transfer - { - /// - /// Initializes a new instance of the class. - /// - /// The address. - /// The value. - /// The message. - /// The tag. - public Transfer(string address, long value, string message, string tag) - { - Address = address; - Value = value; - Message = message; - Tag = tag; - } - - /// - /// Gets or sets the address. - /// - /// - /// The address. - /// - public string Address { get; set; } - - /// - /// Gets or sets the hash. - /// - /// - /// The hash. - /// - public string Hash { get; set; } - - /// - /// Gets or sets the persistence. - /// - /// - /// The persistence. - /// - public int Persistence { get; set; } - - /// - /// Gets or sets the timestamp. - /// - /// - /// The timestamp. - /// - public string Timestamp { get; set; } - - /// - /// Gets or sets the value. - /// - /// - /// The value. - /// - public long Value { get; set; } - - /// - /// Gets or sets the message. - /// - /// - /// The message. - /// - public string Message { get; set; } - - /// - /// Gets or sets the tag. - /// - /// - /// The tag. - /// - public string Tag { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Address)}: {Address}, {nameof(Hash)}: {Hash}, {nameof(Message)}: {Message}, {nameof(Persistence)}: {Persistence}, {nameof(Tag)}: {Tag}, {nameof(Timestamp)}: {Timestamp}, {nameof(Value)}: {Value}"; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Pow/Curl.cs b/IotaCSharpApi/Api/Pow/Curl.cs deleted file mode 100644 index 090750b..0000000 --- a/IotaCSharpApi/Api/Pow/Curl.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System; -using System.ComponentModel; -using Iota.Lib.CSharp.Api.Utils; - -namespace Iota.Lib.CSharp.Api.Pow -{ - /// - /// (c) 2016 Come-from-Beyond - /// Curl belongs to the sponge function family. - /// - public class Curl : Sponge - { - private const int StateLength = 3 * HashLength; - - private const int NumberOfRoundsP81 = 81; - private const int NumberOfRoundsP27 = 27; - - private static readonly int[] TruthTable = {1, 0, -1, 2, 1, -1, 0, 2, -1, 1, 0}; - private readonly int _numberOfRounds; - private readonly int[] _scratchpad = new int[StateLength]; - private readonly long[] _stateHigh; - private readonly long[] _stateLow; - - /// - /// - /// - /// - /// - public Curl(bool pair, CurlMode mode) - { - switch (mode) - { - case CurlMode.CurlP27: - _numberOfRounds = NumberOfRoundsP27; - break; - case CurlMode.CurlP81: - _numberOfRounds = NumberOfRoundsP81; - break; - default: - throw new InvalidEnumArgumentException("Only Curl-P-27 and Curl-P-81 are supported."); - } - - if (pair) - { - _stateHigh = new long[StateLength]; - _stateLow = new long[StateLength]; - State = null; - Set(); - } - else - { - State = new int[StateLength]; - _stateHigh = null; - _stateLow = null; - } - } - - /// - /// - /// - /// - public Curl(CurlMode mode) - { - switch (mode) - { - case CurlMode.CurlP27: - _numberOfRounds = NumberOfRoundsP27; - break; - case CurlMode.CurlP81: - _numberOfRounds = NumberOfRoundsP81; - break; - default: - throw new InvalidEnumArgumentException("Only Curl-P-27 and Curl-P-81 are supported."); - } - - State = new int[StateLength]; - _stateHigh = null; - _stateLow = null; - } - - /// - /// Gets or sets the state. - /// - /// - /// The state. - /// - public int[] State { get; set; } - - /// - /// Absorbs the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// the ICurl instance (used for method chaining) - public override void Absorb(int[] trits, int offset, int length) - { - do - { - Array.Copy(trits, offset, State, 0, length < HashLength ? length : HashLength); - Transform(); - offset += HashLength; - } while ((length -= HashLength) > 0); - } - - private void Transform() - { - var scratchpadIndex = 0; - - for (var round = 0; round < _numberOfRounds; round++) - { - Array.Copy(State, 0, _scratchpad, 0, StateLength); - for (var stateIndex = 0; stateIndex < StateLength; stateIndex++) - { - var prevScratchpadIndex = scratchpadIndex; - if (scratchpadIndex < 365) - scratchpadIndex += 364; - else - scratchpadIndex += -365; - - State[stateIndex] = - TruthTable[ - _scratchpad[prevScratchpadIndex] - + (_scratchpad[scratchpadIndex] << 2) - + 5]; - } - } - } - - /// - /// Resets this state. - /// - /// - /// the ICurl instance (used for method chaining) - /// - public override void Reset() - { - for (var stateIndex = 0; stateIndex < StateLength; stateIndex++) State[stateIndex] = 0; - } - - /// - /// - /// - /// - public void Reset(bool pair) - { - if (pair) - Set(); - else - Reset(); - } - - /// - /// Squeezes the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// - /// the squeezed trits - /// - public override void Squeeze(int[] trits, int offset, int length) - { - do - { - Array.Copy(State, 0, trits, offset, length < HashLength ? length : HashLength); - Transform(); - offset += HashLength; - } while ((length -= HashLength) > 0); - } - - /// - /// Clones this instance. - /// - /// a new instance - public override ICurl Clone() - { - return new Curl(CurlMode.CurlP81); - } - - /// - /// - /// - /// - /// - public void Absorb(Tuple pair, int offset, int length) - { - int o = offset, l = length; - do - { - Array.Copy(pair.Item1, o, _stateLow, 0, l < HashLength ? l : HashLength); - Array.Copy(pair.Item2, o, _stateHigh, 0, l < HashLength ? l : HashLength); - - PairTransform(); - o += HashLength; - } while ((l -= HashLength) > 0); - } - - /// - /// - /// - /// - /// - /// - public Tuple Squeeze(Tuple pair, int offset, int length) - { - int o = offset, l = length; - var low = pair.Item1; - var hi = pair.Item2; - do - { - Array.Copy(_stateLow, 0, low, o, l < HashLength ? l : HashLength); - Array.Copy(_stateHigh, 0, hi, o, l < HashLength ? l : HashLength); - - PairTransform(); - o += HashLength; - } while ((l -= HashLength) > 0); - - return new Tuple(low, hi); - } - - #region Private - - private void Set() - { - for (var i = 0; i < _stateLow.Length; i++) _stateLow[i] = (long) Converter.HIGH_LONG_BITS; - - for (var i = 0; i < _stateHigh.Length; i++) _stateHigh[i] = (long) Converter.HIGH_LONG_BITS; - } - - private void PairTransform() - { - var curlScratchpadLow = new long[StateLength]; - var curlScratchpadHigh = new long[StateLength]; - var curlScratchpadIndex = 0; - for (var round = _numberOfRounds; round-- > 0;) - { - Array.Copy(_stateLow, 0, curlScratchpadLow, 0, StateLength); - Array.Copy(_stateHigh, 0, curlScratchpadHigh, 0, StateLength); - for (var curlStateIndex = 0; curlStateIndex < StateLength; curlStateIndex++) - { - var alpha = curlScratchpadLow[curlScratchpadIndex]; - var beta = curlScratchpadHigh[curlScratchpadIndex]; - var gamma = curlScratchpadHigh[curlScratchpadIndex += curlScratchpadIndex < 365 ? 364 : -365]; - var delta = (alpha | ~gamma) & (curlScratchpadLow[curlScratchpadIndex] ^ beta); - _stateLow[curlStateIndex] = ~delta; - _stateHigh[curlStateIndex] = (alpha ^ gamma) | delta; - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Pow/CurlMode.cs b/IotaCSharpApi/Api/Pow/CurlMode.cs deleted file mode 100644 index c41ef13..0000000 --- a/IotaCSharpApi/Api/Pow/CurlMode.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Pow -{ - /// - /// - /// - public enum CurlMode - { - /// - /// - /// - CurlP81, - /// - /// - /// - CurlP27, - /// - /// - /// - Kerl - } -} diff --git a/IotaCSharpApi/Api/Pow/ICurl.cs b/IotaCSharpApi/Api/Pow/ICurl.cs deleted file mode 100644 index 5885e86..0000000 --- a/IotaCSharpApi/Api/Pow/ICurl.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Pow -{ - /// - /// This interface abstracts the curl hashing algorithm - /// - public interface ICurl - { - /// - /// Absorbs the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// the ICurl instance (used for method chaining) - void Absorb(int[] trits, int offset, int length); - - /// - /// Absorbs the specified trits. - /// - /// The trits. - /// the ICurl instance (used for method chaining) - void Absorb(int[] trits); - - /// - /// Squeezes the specified trits. - /// - /// The trits. - /// The offset to start from. - /// The length. - /// the squeezed trits - void Squeeze(int[] trits, int offset, int length); - - /// - /// Squeezes the specified trits. - /// - /// The trits. - /// the squeezed trits - void Squeeze(int[] trits); - - - /// - /// Resets this state. - /// - /// the ICurl instance (used for method chaining) - void Reset(); - - /// - /// - /// - /// - ICurl Clone(); - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Pow/Kerl.cs b/IotaCSharpApi/Api/Pow/Kerl.cs deleted file mode 100644 index 84e8349..0000000 --- a/IotaCSharpApi/Api/Pow/Kerl.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using Iota.Lib.CSharp.Api.Exception; -using Iota.Lib.CSharp.Api.Utils; -using Org.BouncyCastle.Crypto.Digests; - -namespace Iota.Lib.CSharp.Api.Pow -{ - /// - /// - public class Kerl : Sponge - { - private const int BitHashLength = 384; - private const int ByteHashLength = BitHashLength / 8; - - private readonly byte[] _byteState; - - private readonly KeccakDigest _keccak; - private readonly int[] _tritState; - - /// - /// - /// - public Kerl() - { - _keccak = new KeccakDigest(BitHashLength); - _byteState = new byte[ByteHashLength]; - _tritState = new int[HashLength]; - } - - /// - /// - /// - /// - public override void Reset() - { - _keccak.Reset(); - } - - /// - /// - /// - /// - public override ICurl Clone() - { - return new Kerl(); - } - - /// - /// - /// - /// - /// - /// - /// - public override void Absorb(int[] trits, int offset, int length) - { - if (length % 243 != 0) - throw new IllegalStateException("Illegal length: " + length); - - do - { - //copy trits[offset:offset+length] - Array.Copy(trits, offset, _tritState, 0, HashLength); - - //convert to bits - _tritState[HashLength - 1] = 0; - FixedBigIntConverter.FromTritsToBytes(_tritState, _byteState); - //BigIntConverter.BytesFromBigInt( - // BigIntConverter.BigIntFromTrits(_tritState, 0, HashLength), - // _byteState, 0, ByteHashLength); - - //run keccak - _keccak.BlockUpdate(_byteState, 0, _byteState.Length); - offset += HashLength; - } while ((length -= HashLength) > 0); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public override void Squeeze(int[] trits, int offset, int length) - { - if (length % 243 != 0) throw new IllegalStateException("Illegal length: " + length); - - do - { - _keccak.DoFinal(_byteState, 0); - - //convert to trits - FixedBigIntConverter.FromBytesToTrits(_byteState, _tritState); - //BigIntConverter.TritsFromBigInt( - // BigIntConverter.BigIntFromBytes(_byteState, 0, ByteHashLength), - // _tritState, 0, HashLength); - - //copy with offset - _tritState[HashLength - 1] = 0; - Array.Copy(_tritState, 0, trits, offset, HashLength); - - //calculate hash again - for (var i = _byteState.Length; i-- > 0;) _byteState[i] = (byte) (_byteState[i] ^ 0xFF); - - _keccak.BlockUpdate(_byteState, 0, _byteState.Length); - offset += HashLength; - } while ((length -= HashLength) > 0); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Pow/PearlDiverLocalPoW.cs b/IotaCSharpApi/Api/Pow/PearlDiverLocalPoW.cs deleted file mode 100644 index 77bdc96..0000000 --- a/IotaCSharpApi/Api/Pow/PearlDiverLocalPoW.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Iota.Lib.CSharp.Api.Core; -using Iota.Lib.CSharp.Api.Exception; -using Iota.Lib.CSharp.Api.Utils; - -namespace Iota.Lib.CSharp.Api.Pow -{ - /// - /// - public class PearlDiverLocalPoW : ILocalPoW - { - private readonly PearlDiver _pearlDiver = new PearlDiver(); - - /// - /// - /// - /// - /// - /// - public string PerformPoW(string trytes, int minWeightMagnitude) - { - var trits = Converter.ToTrits(trytes); - - if (!_pearlDiver.Search(trits, minWeightMagnitude, 0)) - throw new IllegalStateException("PearlDiver search failed"); - - return Converter.ToTrytes(trits); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Pow/Sponge.cs b/IotaCSharpApi/Api/Pow/Sponge.cs deleted file mode 100644 index 129419e..0000000 --- a/IotaCSharpApi/Api/Pow/Sponge.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Pow -{ - /// - /// - /// - public abstract class Sponge : ICurl - { - - /// - /// - /// - public const int HashLength = 243; - - /// - /// - /// - /// - /// - /// - public abstract void Absorb(int[] trits, int offset, int length); - - /// - /// - /// - /// - public void Absorb(int[] trits) => Absorb(trits,0,trits.Length); - - /// - /// - /// - /// - /// - /// - /// - public abstract void Squeeze(int[] trits, int offset, int length); - - /// - /// - /// - /// - /// - public void Squeeze(int[] trits) => Squeeze(trits,0,trits.Length); - - /// - /// - /// - /// - public abstract void Reset(); - - /// - /// - /// - /// - public abstract ICurl Clone(); - } -} diff --git a/IotaCSharpApi/Api/Utils/ArrayUtils.cs b/IotaCSharpApi/Api/Utils/ArrayUtils.cs deleted file mode 100644 index d893719..0000000 --- a/IotaCSharpApi/Api/Utils/ArrayUtils.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Iota.Lib.CSharp.Api.Utils -{ - internal class ArrayUtils - { - public static IEnumerable SliceRow(T[,] array, int row) - { - for (var i = array.GetLowerBound(1); i <= array.GetUpperBound(1); i++) - { - yield return array[row, i]; - } - } - - public static T[] SubArray(T[] data, int startIndex, int endIndex) - { - int length = endIndex - startIndex; - T[] result = new T[endIndex - startIndex]; - Array.Copy(data, startIndex, result, 0, length); - return result; - } - - public static T[] SubArray2(T[] data, int index, int length) - { - T[] result = new T[length]; - Array.Copy(data, index, result, 0, length); - return result; - } - } -} diff --git a/IotaCSharpApi/Api/Utils/BigIntConverter.cs b/IotaCSharpApi/Api/Utils/BigIntConverter.cs deleted file mode 100644 index 298e0b9..0000000 --- a/IotaCSharpApi/Api/Utils/BigIntConverter.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using Org.BouncyCastle.Math; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// - /// - public class BigIntConverter - { - /// - /// - /// - /// - /// - /// - /// - public static BigInteger BigIntFromTrits(int[] trits, int offset, int size) - { - var value = BigInteger.Zero; - - for (var i = size; i-- > 0;) - value = value.Multiply(BigInteger.ValueOf(Converter.Radix)).Add(BigInteger.ValueOf(trits[offset + i])); - - return value; - } - - /// - /// - /// - /// - /// - /// - /// - public static BigInteger BigIntFromBytes(byte[] bytes, int offset, int size) - { - return new BigInteger(bytes, offset, size); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static void TritsFromBigInt(BigInteger value, int[] destination, int offset, int size) - { - if (destination.Length - offset < size) throw new ArgumentException("Destination array has invalid size"); - - var absoluteValue = value.CompareTo(BigInteger.Zero) < 0 ? value.Negate() : value; - for (var i = 0; i < size; i++) - { - var divRemainder = absoluteValue.DivideAndRemainder(BigInteger.ValueOf(Converter.Radix)); - var remainder = divRemainder[1].IntValue; - absoluteValue = divRemainder[0]; - - if (remainder > Converter.MaxTritValue) - { - remainder = Converter.MinTritValue; - absoluteValue = absoluteValue.Add(BigInteger.One); - } - - destination[offset + i] = remainder; - } - - if (value.CompareTo(BigInteger.Zero) < 0) - for (var i = 0; i < size; i++) - destination[offset + i] = -destination[offset + i]; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static void BytesFromBigInt(BigInteger value, byte[] destination, int offset,int size) - { - if (destination.Length - offset < size) - throw new ArgumentException("Destination array has invalid size."); - - var bytes = value.ToByteArray(); - var i = 0; - while (i + bytes.Length < size) destination[i++] = (byte) ((sbyte) bytes[0] < 0 ? -1 : 0); - - for (var j = bytes.Length; j-- > 0;) destination[i++] = bytes[bytes.Length - 1 - j]; - } - } -} diff --git a/IotaCSharpApi/Api/Utils/Checksum.cs b/IotaCSharpApi/Api/Utils/Checksum.cs deleted file mode 100644 index 964f7a8..0000000 --- a/IotaCSharpApi/Api/Utils/Checksum.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Iota.Lib.CSharp.Api.Exception; -using Iota.Lib.CSharp.Api.Pow; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// This class defines utility methods to add/remove the checksum to/from an address - /// - public static class Checksum - { - /// - /// Adds the checksum to the specified address - /// - /// An address without checksum - /// The address with the appended checksum - /// is thrown when an invalid address is provided - public static string AddChecksum(string address) - { - InputValidator.CheckAddress(address); - var addressWithChecksum = address; - addressWithChecksum += CalculateChecksum(address); - return addressWithChecksum; - } - - - /// - /// Removes the checksum from the specified address with checksum - /// - /// The address with checksum or without checksum. - /// the specified address without checksum - /// is thrown when the specified address is not an address with checksum - public static string RemoveChecksum(this string address) - { - if (IsAddressWithChecksum(address)) return GetAddress(address); - - if (IsAddressWithoutChecksum(address)) return address; - - throw new InvalidAddressException(address); - } - - - internal static string GetAddress(string addressWithChecksum) - { - return addressWithChecksum.Substring(0, Constants.AddressLengthWithoutChecksum); - } - - /// - /// Determines whether the specified address with checksum has a valid checksum. - /// - /// The address with checksum. - /// - /// true if the specified address with checksum has a valid checksum [the specified address with checksum]; - /// otherwise, false. - /// - public static bool IsValidChecksum(this string addressWithChecksum) - { - var addressWithoutChecksum = RemoveChecksum(addressWithChecksum); - var adressWithRecalculateChecksum = addressWithoutChecksum + CalculateChecksum(addressWithoutChecksum); - return adressWithRecalculateChecksum.Equals(addressWithChecksum); - } - - - private static bool IsAddressWithChecksum(string addressWithChecksum) - { - return InputValidator.IsAddress(addressWithChecksum) && - addressWithChecksum.Length == Constants.AddressLengthWithChecksum; - } - - private static bool IsAddressWithoutChecksum(string address) - { - return InputValidator.CheckAddress(address) && address.Length == Constants.AddressLengthWithoutChecksum; - } - - private static string CalculateChecksum(string address) - { - // TODO inject curl - ICurl curl = new Kerl(); - curl.Reset(); - curl.Absorb(Converter.ToTrits(address)); - var checksumTrits = new int[Sponge.HashLength]; - curl.Squeeze(checksumTrits); - var checksum = Converter.ToTrytes(checksumTrits); - return checksum.Substring(72, 9); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/Constants.cs b/IotaCSharpApi/Api/Utils/Constants.cs deleted file mode 100644 index ba1121f..0000000 --- a/IotaCSharpApi/Api/Utils/Constants.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// This class defines different constants that are used accros the library - /// - public static class Constants - { - /// - /// This String contains all possible characters of the tryte alphabet - /// - public static readonly string TryteAlphabet = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - /// - /// The maximum seed length - /// - public static readonly int SeedLengthMax = 81; - - /// - /// This String represents the empty hash consisting of '9' - /// - public static readonly string EmptyHash = - "999999999999999999999999999999999999999999999999999999999999999999999999999999999"; - - /// - /// The length of an address without checksum - /// - public static readonly int AddressLengthWithoutChecksum = 81; - - /// - /// The address length with checksum - /// - public static readonly int AddressLengthWithChecksum = 90; - - /// - /// The length of an message - /// - public static int MessageLength = 2187; - /// - /// - /// - public static int TagLength = 27; - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/Converter.cs b/IotaCSharpApi/Api/Utils/Converter.cs deleted file mode 100644 index 0625819..0000000 --- a/IotaCSharpApi/Api/Utils/Converter.cs +++ /dev/null @@ -1,378 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// This class provides a set of utility methods to are used to convert between different formats - /// - public class Converter - { - // ReSharper disable InconsistentNaming - /// - /// - /// - public static readonly uint HIGH_INTEGER_BITS = 0xFFFFFFFF; - - /// - /// - /// - public static readonly ulong HIGH_LONG_BITS = 0xFFFFFFFFFFFFFFFFL; - // ReSharper restore InconsistentNaming - - /// - /// The radix - /// - public static readonly int Radix = 3; - - /// - /// The maximum trit value - /// - public static readonly int MaxTritValue = (Radix - 1) / 2; - - /// - /// The minimum trit value - /// - public static readonly int MinTritValue = -MaxTritValue; - - /// - /// The number of trits in a byte - /// - public static readonly int NumberOfTritsInAByte = 5; - - /// - /// The number of trits in a tryte - /// - public static readonly int NumberOfTritsInATryte = 3; - - static readonly int[][] ByteToTritsMappings = new int[243][]; //why 243? max 121 - static readonly int[][] TryteToTritsMappings = new int[27][]; - - static Converter() - { - int[] trits = new int[NumberOfTritsInAByte]; - - for (int i = 0; i < 243; i++) - { - ByteToTritsMappings[i] = new int[NumberOfTritsInAByte]; - ByteToTritsMappings[i] = new int[NumberOfTritsInAByte]; - Array.Copy(trits, ByteToTritsMappings[i], NumberOfTritsInAByte); - Increment(trits, NumberOfTritsInAByte); - } - - for (int i = 0; i < 27; i++) - { - TryteToTritsMappings[i] = new int[NumberOfTritsInATryte]; - Array.Copy(trits, TryteToTritsMappings[i], NumberOfTritsInATryte); - Increment(trits, NumberOfTritsInATryte); - } - } - - /// - /// Converts the specified trits array to bytes - /// - /// The trits. - /// The offset to start from. - /// The size. - /// - public static byte[] ToBytes(int[] trits, int offset, int size) - { - byte[] bytes = new byte[(size + NumberOfTritsInAByte - 1) / NumberOfTritsInAByte]; - for (int i = 0; i < bytes.Length; i++) - { - int value = 0; - for ( - int j = (size - i * NumberOfTritsInAByte) < 5 - ? (size - i * NumberOfTritsInAByte) - : NumberOfTritsInAByte; - j-- > 0;) - { - value = value * Radix + trits[offset + i * NumberOfTritsInAByte + j]; - } - - bytes[i] = (byte) value; - } - - return bytes; - } - - /// - /// Converts the specified trits to trytes - /// - /// The trits. - /// - public static byte[] ToBytes(int[] trits) - { - return ToBytes(trits, 0, trits.Length); - } - - /// - /// Gets the trits from the specified bytes and stores it into the provided trits array - /// - /// The bytes. - /// The trits. - public static void GetTrits(sbyte[] bytes, int[] trits) - { - int offset = 0; - for (int i = 0; i < bytes.Length && offset < trits.Length; i++) - { - Array.Copy( - ByteToTritsMappings[bytes[i] < 0 ? (bytes[i] + ByteToTritsMappings.Length) : bytes[i]], 0, - trits, offset, - trits.Length - offset < NumberOfTritsInAByte - ? (trits.Length - offset) - : NumberOfTritsInAByte); - - offset += NumberOfTritsInAByte; - } - - while (offset < trits.Length) - { - trits[offset++] = 0; - } - } - - /// - /// Converts the specified trinary encoded string into a trits array of the specified length. - /// If the trytes string results in a shorter then specified trits array, then the remainder is padded we zeroes - /// - /// The trytes. - /// The length. - /// a trits array - public static int[] ToTrits(string trytes, int length) - { - int[] tritss = ToTrits(trytes); - - List tritsList = new List(); - - foreach (int i in tritss) - tritsList.Add(i); - - while (tritsList.Count < length) - tritsList.Add(0); - - return tritsList.ToArray(); - } - - - /// - /// Converts the specified trinary encoded trytes string to trits - /// - /// The trytes. - /// - public static int[] ToTrits(string trytes) - { - int[] d = new int[3 * trytes.Length]; - for (int i = 0; i < trytes.Length; i++) - { - Array.Copy(TryteToTritsMappings[Constants.TryteAlphabet.IndexOf(trytes[i])], 0, d, - i * NumberOfTritsInATryte, NumberOfTritsInATryte); - } - - return d; - } - - /// - /// - /// - /// - /// - /// - public static int[] ToTrits(long trytes, int length) - { - int[] tritss = ToTrits(trytes); - - List tritsList = new List(tritss); - - while (tritsList.Count < length) - tritsList.Add(0); - - return tritsList.ToArray(); - } - - /// - /// - /// - /// - /// - public static int[] ToTrits(long trytes) - { - if (trytes == 0) - return new[] {0}; - - var tritsList = new List(); - - while (trytes != 0) - { - var remainder = (int) (trytes % Radix); - trytes /= Radix; - - if (remainder > MaxTritValue) - { - remainder = MinTritValue; - trytes += 1; - } - else if (remainder < MinTritValue) - { - remainder = MaxTritValue; - trytes -= 1; - } - - tritsList.Add(remainder); - } - - return tritsList.ToArray(); - } - - /// - /// Copies the trits from the input string into the destination array - /// - /// The input string - /// The destination array. - /// - public static int[] CopyTrits(string input, int[] destination) - { - for (int i = 0; i < input.Length; i++) - { - int index = Constants.TryteAlphabet.IndexOf(input[i]); - destination[i * 3] = TryteToTritsMappings[index][0]; - destination[i * 3 + 1] = TryteToTritsMappings[index][1]; - destination[i * 3 + 2] = TryteToTritsMappings[index][2]; - } - - return destination; - } - - /// - /// Copies the trits in long representation into the destination array - /// - /// The value. - /// The destination array. - /// The offset from which copying is started. - /// The size. - public static void CopyTrits(long value, int[] destination, int offset, int size) - { - long absoluteValue = value < 0 ? -value : value; - for (int i = 0; i < size; i++) - { - int remainder = (int) (absoluteValue % Radix); - absoluteValue /= Radix; - if (remainder > MaxTritValue) - { - remainder = MinTritValue; - absoluteValue++; - } - - destination[offset + i] = remainder; - } - - if (value < 0) - { - for (int i = 0; i < size; i++) - { - destination[offset + i] = -destination[offset + i]; - } - } - } - - /// - /// Converts the trits array to a trytes string - /// - /// The trits. - /// The offset from which copying is started. - /// The size. - /// a trytes string - public static string ToTrytes(int[] trits, int offset, int size) - { - StringBuilder trytes = new StringBuilder(); - for (int i = 0; i < (size + NumberOfTritsInATryte - 1) / NumberOfTritsInATryte; i++) - { - int j = trits[offset + i * 3] + trits[offset + i * 3 + 1] * 3 + trits[offset + i * 3 + 2] * 9; - if (j < 0) - { - j += Constants.TryteAlphabet.Length; - } - - trytes.Append(Constants.TryteAlphabet[j]); - } - - return trytes.ToString(); - } - - /// - /// Converts the trits array to a trytes string - /// - /// The trits. - /// a trytes string - public static string ToTrytes(int[] trits) - { - return ToTrytes(trits, 0, trits.Length); - } - - /// - /// Converts the specified trits array to trytes in integer representation - /// - /// The trits. - /// The offset. - /// trytes in integer representation - public static int ToTryteValue(int[] trits, int offset) - { - return trits[offset] + trits[offset + 1] * 3 + trits[offset + 2] * 9; - } - - /// - /// Converts the specified trits to its corresponding integer value - /// - /// The trits. - /// an integer value representing the corresponding trits - public static int ToValue(int[] trits) - { - int value = 0; - - for (int i = trits.Length; i-- > 0;) - { - value = value * 3 + trits[i]; - } - - return value; - } - - /// - /// Converts the specified trits to its corresponding integer value - /// - /// The trits. - /// - public static long ToLongValue(int[] trits) - { - long value = 0; - - for (int i = trits.Length; i-- > 0;) - { - value = value * 3 + trits[i]; - } - - return value; - } - - /// - /// Increments the specified trits. - /// - /// The trits. - /// The size. - public static void Increment(int[] trits, int size) - { - for (int i = 0; i < size; i++) - { - if (++trits[i] > MaxTritValue) - { - trits[i] = MinTritValue; - } - else - { - break; - } - } - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/InputValidator.cs b/IotaCSharpApi/Api/Utils/InputValidator.cs deleted file mode 100644 index 6956e63..0000000 --- a/IotaCSharpApi/Api/Utils/InputValidator.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Iota.Lib.CSharp.Api.Exception; -using Iota.Lib.CSharp.Api.Model; -using RestSharp.Extensions; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// This class provides methods to validate the parameters of different iota API methods - /// - public static class InputValidator - { - /// - /// Determines whether the specified string is an adrdress. - /// - /// The address. - /// - /// true if the specified string is an address; otherwise, false. - /// - public static bool IsAddress(string address) - { - if (address.Length == Constants.AddressLengthWithoutChecksum || - address.Length == Constants.AddressLengthWithChecksum) - { - return IsTrytes(address, address.Length); - } - return false; - } - - /// - /// - /// - /// - /// - public static bool IsHash(string hash) - { - return IsTrytes(hash, 81); - } - - /// - /// Checks whether the specified address is an address and throws and exception if the address is invalid - /// - /// address to check - /// exception which is thrown when the address is invalid - public static bool CheckAddress(string address) - { - if (!IsAddress(address)) - throw new InvalidAddressException(address); - - return true; - } - - /// - /// Determines whether the specified string represents an integer value. - /// - /// The value. - /// - /// true the specified string represents an integer value; otherwise, false. - /// - public static bool IsValue(string value) - { - // ReSharper disable once NotAccessedVariable - long tempValue; - return long.TryParse(value, out tempValue); - //return Regex.IsMatch(value, @"^(-){0,1}\d+$"); - } - - /// - /// Determines whether the specified array contains only valid hashes - /// - /// The hashes. - /// - /// true the specified array contains only valid hashes; otherwise, false. - /// - public static bool IsArrayOfHashes(string[] hashes) - { - if (hashes == null) - return false; - - foreach (string hash in hashes) - { - // Check if address with checksum - if (hash.Length == 90) - { - if (!IsTrytes(hash, 90)) - { - return false; - } - } - else - { - if (!IsTrytes(hash, 81)) - { - return false; - } - } - } - return true; - } - - /// - /// Determines whether the specified string contains only characters from the trytes alphabet (see ) - /// - /// The trytes. - /// The length. - /// - /// true if the specified trytes are trytes otherwise, false. - /// - public static bool IsTrytes(string trytes, int length) - { - string regex = "^[9A-Z]{" + (length == 0 ? "0," : length.ToString()) + "}$"; - var regexTrytes = new Regex(regex); - return regexTrytes.IsMatch(trytes); - } - - /// - /// Determines whether the specified string array contains only trytes - /// - /// The trytes. - /// The length. - /// - /// true if the specified array contains only valid trytes otherwise, false. - /// - public static bool IsArrayOfTrytes(string[] trytes, int length ) - { - return trytes.ToList().TrueForAll(element => IsTrytes(element, length)); - } - - /// - /// Determines whether the specified transfers are valid - /// - /// The transfers. - /// - /// true if the specified transfers are valid; otherwise, false. - /// - public static bool IsTransfersCollectionValid(ICollection transfers) - { - foreach (Transfer transfer in transfers) - { - if (!IsValidTransfer(transfer)) - { - return false; - } - } - return true; - } - - /// - /// Determines whether the specified transfer is valid. - /// - /// The transfer. - /// - /// true if the specified transfer is valid; otherwise, false. - /// - public static bool IsValidTransfer(Transfer transfer) - { - if (!IsAddress(transfer.Address)) - { - return false; - } - - // Check if message is correct trytes of any length - if (!IsTrytes(transfer.Message, 0)) - { - return false; - } - - // Check if tag is correct trytes of {0,27} trytes - return IsTrytes(transfer.Tag, 27); - } - - /// - /// Checks the specified specified transfers are valid. If not, an exception is thrown. - /// - /// The transactions array. - /// Not a transfer array - public static void CheckTransferArray(Transfer[] transactionsArray) - { - if (!IsTransfersCollectionValid(transactionsArray.ToList())) - throw new System.Exception("Not a transfer array"); - } - - /// - /// Checks if the seed is valid. If not, an exception is thrown. - /// - /// The seed. - /// - /// Invalid Seed: Format not in trytes - /// or - /// Invalid Seed: Seed too long - /// - public static void CheckIfValidSeed(string seed) - { - // validate the seed - if (!IsTrytes(seed, 0)) - { - throw new IllegalStateException("Invalid Seed: Format not in trytes"); - } - - // validate & if needed pad seed - if (seed.Length > 81) - { - throw new IllegalStateException("Invalid Seed: Seed too long"); - } - } - - /// - /// Pads the seed if necessary. - /// - /// The seed. - /// - public static string PadSeedIfNecessary(string seed) - { - while (seed.Length < Constants.AddressLengthWithoutChecksum) seed += 9; - return seed; - } - - /// - /// Checks if the specified array is an array of trytes. If not an exception is thrown. - /// - /// The trytes. - /// - public static void CheckIfArrayOfTrytes(string[] trytes) - { - if(!IsArrayOfTrytes(trytes, 2673)) - throw new InvalidTryteException(); - } - - /// - /// Determines whether the specified string consist only of '9'. - /// - /// The trytes. - /// The length. - /// - /// true if the specified string consist only of '9'; otherwise, false. - /// - public static bool IsNinesTrytes(string trytes, int length) - { - return trytes.Matches("^[9]{" + (length == 0 ? "0," : length.ToString()) + "}$"); - } - - /// - /// - /// - /// - /// - public static bool IsValidSeed(string seed) - { - return IsTrytes(seed, seed.Length); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/IotaApiUtils.cs b/IotaCSharpApi/Api/Utils/IotaApiUtils.cs deleted file mode 100644 index 5fa3ecc..0000000 --- a/IotaCSharpApi/Api/Utils/IotaApiUtils.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Pow; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// - /// - public class IotaApiUtils - { - /// - /// Generates a new address - /// - /// The tryte-encoded seed. It should be noted that this seed is not transferred. - /// The secuirty level of private key / seed. - /// The index to start search from. If the index is provided, the generation of the address is not deterministic. - /// The adds 9-tryte address checksum - /// The curl instance. - /// An String with address. - public static string NewAddress(string seed, int security, int index, bool checksum, ICurl curl) - { - if (security < 1) - throw new ArgumentException("invalid security level provided"); - - Signing signing = new Signing(curl); - int[] key = signing.Key(Converter.ToTrits(seed), index, security); - int[] digests = signing.Digests(key); - int[] addressTrits = signing.Address(digests); - - string address = Converter.ToTrytes(addressTrits); - - if (checksum) - address = Checksum.AddChecksum(address); - - return address; - } - - internal static List SignInputsAndReturn(string seed, - List inputs, - Bundle bundle, - List signatureFragments, ICurl curl) - { - bundle.FinalizeBundle(curl); - bundle.AddTrytes(signatureFragments); - - // SIGNING OF INPUTS - // - // Here we do the actual signing of the inputs - // Iterate over all bundle transactions, find the inputs - // Get the corresponding private key and calculate the signatureFragment - for (int i = 0; i < bundle.Transactions.Count; i++) - { - if (bundle.Transactions[i].Value < 0) - { - string thisAddress = bundle.Transactions[i].Address; - - // Get the corresponding keyIndex of the address - int keyIndex = 0; - foreach (Input input in inputs) - { - if (input.Address.Equals(thisAddress)) - { - keyIndex = input.KeyIndex; - break; - } - } - - string bundleHash = bundle.Transactions[i].Bundle; - - // Get corresponding private key of address - int[] key = new Signing(curl).Key(Converter.ToTrits(seed), keyIndex, 2); - - // First 6561 trits for the firstFragment - int[] firstFragment = ArrayUtils.SubArray2(key, 0, 6561); - - // Get the normalized bundle hash - int[] normalizedBundleHash = bundle.NormalizedBundle(bundleHash); - - // First bundle fragment uses 27 trytes - int[] firstBundleFragment = ArrayUtils.SubArray2(normalizedBundleHash, 0, 27); - - // Calculate the new signatureFragment with the first bundle fragment - int[] firstSignedFragment = new Signing(curl).SignatureFragment(firstBundleFragment, firstFragment); - - // Convert signature to trytes and assign the new signatureFragment - bundle.Transactions[i].SignatureMessageFragment = Converter.ToTrytes(firstSignedFragment); - - // Because the signature is > 2187 trytes, we need to - // find the second transaction to add the remainder of the signature - for (int j = 0; j < bundle.Transactions.Count; j++) - { - // Same address as well as value = 0 (as we already spent the input) - if (bundle.Transactions[j].Address.Equals(thisAddress) && - bundle.Transactions[j].Value == 0) - { - // Use the second 6562 trits - int[] secondFragment = ArrayUtils.SubArray2(key, 6561, 6561); - - // The second 27 to 54 trytes of the bundle hash - int[] secondBundleFragment = ArrayUtils.SubArray2(normalizedBundleHash, 27, 27); - - // Calculate the new signature - int[] secondSignedFragment = new Signing(curl).SignatureFragment(secondBundleFragment, - secondFragment); - - // Convert signature to trytes and assign it again to this bundle entry - bundle.Transactions[j].SignatureMessageFragment = (Converter.ToTrytes(secondSignedFragment)); - } - } - } - } - - List bundleTrytes = new List(); - - // Convert all bundle entries into trytes - foreach (Transaction tx in bundle.Transactions) - { - bundleTrytes.Add(tx.ToTransactionTrytes()); - } - - bundleTrytes.Reverse(); - return bundleTrytes; - } - - internal static long CreateTimeStampNow() - { - return (long) (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/IotaUnitConverter.cs b/IotaCSharpApi/Api/Utils/IotaUnitConverter.cs deleted file mode 100644 index 306a21a..0000000 --- a/IotaCSharpApi/Api/Utils/IotaUnitConverter.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// This class provides methods to convert Iota to different units - /// - public class IotaUnitConverter - { - /// - /// Convert the iota amount - /// - /// amount - /// the source unit e.g. the unit of amount - /// the target unit - /// the specified amount in the target unit - public static double ConvertUnits(long amount, IotaUnits fromUnit, IotaUnits toUnit) - { - long amountInSource = (long) (amount*Math.Pow(10, (int) fromUnit)); - return ConvertUnits(amountInSource, toUnit); - } - - private static double ConvertUnits(long amount, IotaUnits toUnit) - { - int base10NormalizationExponent = (int) toUnit; - return (amount/Math.Pow(10, base10NormalizationExponent)); - } - - /// - /// Finds the optimal unit to display the specified amount in - /// - /// amount - /// the optimal IotaUnit - public static IotaUnits FindOptimalIotaUnitToDisplay(long amount) - { - int length = (amount).ToString().Length; - - if (amount < 0) - { - // do not count "-" sign - length -= 1; - } - - IotaUnits units = IotaUnits.Iota; - - if (length >= 1 && length <= 3) - { - units = IotaUnits.Iota; - } - else if (length > 3 && length <= 6) - { - units = IotaUnits.Kilo; - } - else if (length > 6 && length <= 9) - { - units = IotaUnits.Mega; - } - else if (length > 9 && length <= 12) - { - units = IotaUnits.Giga; - } - else if (length > 12 && length <= 15) - { - units = IotaUnits.Terra; - } - else if (length > 15 && length <= 18) - { - units = IotaUnits.Peta; - } - return units; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/IotaUnits.cs b/IotaCSharpApi/Api/Utils/IotaUnits.cs deleted file mode 100644 index 8b93120..0000000 --- a/IotaCSharpApi/Api/Utils/IotaUnits.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// Iota Units - /// - public enum IotaUnits - { - /// - /// The corresponding value is in iota. Same as 'None' () - /// - Iota = 0, - - /// - /// The corresponding value is in iota. Same as 'Iota' () - /// - None = 0, - - /// - /// 10^3 - /// - Kilo = 3, - - /// - /// 10^6 - /// - Mega = 6, - - /// - /// 10^9 - /// - Giga = 9, - - /// - /// 10^12 - /// - Terra = 12, - - /// - /// 10^15 - /// - Peta = 15, - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/Rest/JsonSerializer.cs b/IotaCSharpApi/Api/Utils/Rest/JsonSerializer.cs deleted file mode 100644 index fab77e9..0000000 --- a/IotaCSharpApi/Api/Utils/Rest/JsonSerializer.cs +++ /dev/null @@ -1,116 +0,0 @@ -#region License - -// Copyright 2010 John Sheehan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#endregion - -#region Acknowledgements - -// Original JsonSerializer contributed by Daniel Crenna (@dimebrain) - -#endregion - -using System; -using System.IO; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using RestSharp.Serializers; - -namespace Iota.Lib.CSharp.Api.Utils.Rest -{ - /// - /// Default JSON serializer for request bodies - /// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes - /// - internal class JsonSerializer : ISerializer - { - private readonly Newtonsoft.Json.JsonSerializer _serializer; - - /// - /// Default serializer - /// - public JsonSerializer() - { - ContentType = "application/json"; - _serializer = new Newtonsoft.Json.JsonSerializer - { - MissingMemberHandling = MissingMemberHandling.Ignore, - NullValueHandling = NullValueHandling.Include, - DefaultValueHandling = DefaultValueHandling.Include - }; - } - - /// - /// Default serializer with overload for allowing custom Json.NET settings - /// - public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer) - { - ContentType = "application/json"; - _serializer = serializer; - } - - /// - /// Serialize the object as JSON - /// - /// Object to serialize - /// JSON as String - public string Serialize(object obj) - { - using (var stringWriter = new StringWriter()) - { - using (var jsonTextWriter = new JsonTextWriter(stringWriter)) - { - //jsonTextWriter.Formatting = Formatting.Indented; - //jsonTextWriter.QuoteChar = '"'; - _serializer.ContractResolver = new LowercaseContractResolver(); - - _serializer.Serialize(jsonTextWriter, obj); - - var result = stringWriter.ToString(); - return result; - } - } - } - - public class LowercaseContractResolver : DefaultContractResolver - { - protected override string ResolvePropertyName(string name) - { - // first letter small to match API naming conventions - return Char.ToLowerInvariant(name[0]) + name.Substring(1); - } - } - - /// - /// Unused for JSON Serialization - /// - public string DateFormat { get; set; } - - /// - /// Unused for JSON Serialization - /// - public string RootElement { get; set; } - - /// - /// Unused for JSON Serialization - /// - public string Namespace { get; set; } - - /// - /// Content type for serialized content - /// - public string ContentType { get; set; } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/Rest/JsonWebClient.cs b/IotaCSharpApi/Api/Utils/Rest/JsonWebClient.cs deleted file mode 100644 index 6065da2..0000000 --- a/IotaCSharpApi/Api/Utils/Rest/JsonWebClient.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Net.Cache; -using System.Text; -using Iota.Lib.CSharp.Api.Core; -using Iota.Lib.CSharp.Api.Exception; -using Newtonsoft.Json; - -namespace Iota.Lib.CSharp.Api.Utils.Rest -{ - internal class JsonWebClient - { - public TResponse GetPOSTResponseSync(Uri uri, string data) - { - Console.WriteLine("request: " + data); - - HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); - - request.ContentType = "application/json"; - request.Accept = "application/json"; - request.Method = "POST"; - request.AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate); - request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore); - request.Headers.Add("X-IOTA-API-Version", "1"); - request.Headers.Add("Origin", "iota.lib.csharp"); - request.Headers.Add("Accept-Language", "de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4 "); - request.KeepAlive = false; - request.Timeout = 300000; - request.ReadWriteTimeout = 300000; - - UTF8Encoding encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(data); - - request.ContentLength = bytes.Length; - - Stream requestStream = request.GetRequestStream(); - { - // Send the data. - requestStream.Write(bytes, 0, bytes.Length); - } - - try - { - using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) - { - using (Stream stream = response.GetResponseStream()) - { - StreamReader reader = new StreamReader(stream, Encoding.UTF8); - string responseString = reader.ReadToEnd(); - - if (response.StatusCode == HttpStatusCode.OK) - { - Console.WriteLine("response: " + responseString); - return JsonConvert.DeserializeObject(responseString); - } - - throw new IotaApiException(JsonConvert.DeserializeObject(responseString).Error); - } - } - } - catch (WebException ex) - { - Console.WriteLine("catched: " + ex + ex.Message); - - using (var stream = ex.Response.GetResponseStream()) - using (var reader = new StreamReader(stream)) - { - String errorResponse = reader.ReadToEnd(); - throw new IotaApiException(JsonConvert.DeserializeObject(errorResponse).Error); - } - } - } - - public void GetPOSTResponseAsync(Uri uri, string data, Action callback) - { - HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); - - request.Method = "POST"; - request.ContentType = "application-type/json;charset=utf-8"; - - UTF8Encoding encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(data); - - request.ContentLength = bytes.Length; - - using (Stream requestStream = request.GetRequestStream()) - { - // Send the data. - requestStream.Write(bytes, 0, bytes.Length); - } - - request.BeginGetResponse((x) => - { - using (HttpWebResponse response = (HttpWebResponse) request.EndGetResponse(x)) - { - if (callback != null) - { - using (Stream stream = response.GetResponseStream()) - { - StreamReader reader = new StreamReader(stream, Encoding.UTF8); - string responseString = reader.ReadToEnd(); - callback(JsonConvert.DeserializeObject(responseString)); - } - } - } - }, request); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/Signing.cs b/IotaCSharpApi/Api/Utils/Signing.cs deleted file mode 100644 index fe1188e..0000000 --- a/IotaCSharpApi/Api/Utils/Signing.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Pow; - -namespace Iota.Lib.CSharp.Api.Utils -{ - //TODO(gjc): add comments - - /// - /// Ask cfb - /// - public class Signing - { - /// - /// - /// - public static int KeyLength = 6561; - - private readonly ICurl _curl; - - /// - /// - /// - public Signing(ICurl curl) - { - // ReSharper disable once ConvertIfStatementToNullCoalescingExpression - if (curl == null) - _curl = new Kerl(); - else - _curl = curl; - } - - /// - /// - public Signing() - { - _curl = new Kerl(); - } - - /// - /// - /// - /// - /// - /// - public int[] Key(int[] seed, int index, int security) - { - var subseed = new int[seed.Length]; - seed.CopyTo(subseed, 0); - - for (var i = 0; i < index; i++) - for (var j = 0; j < 243; j++) - if (++subseed[j] > 1) - subseed[j] = -1; - else - break; - - _curl.Reset(); - _curl.Absorb(subseed, 0, subseed.Length); - _curl.Squeeze(subseed, 0, subseed.Length); - _curl.Reset(); - _curl.Absorb(subseed, 0, subseed.Length); - - var key = new List(); - var buffer = new int[subseed.Length]; - var offset = 0; - - while (security-- > 0) - for (var i = 0; i < 27; i++) - { - _curl.Squeeze(buffer, offset, buffer.Length); - for (var j = 0; j < 243; j++) key.Add(buffer[j]); - } - - return key.ToArray(); - } - - /// - /// - /// - /// - public int[] Digests(int[] key) - { - var digests = new int[(int) Math.Floor((decimal) key.Length / 6561) * 243]; - var buffer = new int[243]; - - for (var i = 0; i < Math.Floor((decimal) key.Length / 6561); i++) - { - var keyFragment = new int[6561]; - Array.Copy(key, i * 6561, keyFragment, 0, 6561); - - for (var j = 0; j < 27; j++) - { - Array.Copy(keyFragment, j * 243, buffer, 0, 243); - for (var k = 0; k < 26; k++) - { - _curl.Reset(); - _curl.Absorb(buffer, 0, buffer.Length); - _curl.Squeeze(buffer, 0, buffer.Length); - } - - for (var k = 0; k < 243; k++) keyFragment[j * 243 + k] = buffer[k]; - } - - _curl.Reset(); - _curl.Absorb(keyFragment, 0, keyFragment.Length); - _curl.Squeeze(buffer, 0, buffer.Length); - - for (var j = 0; j < 243; j++) digests[i * 243 + j] = buffer[j]; - } - - return digests; - } - - /// - /// - /// - /// - /// - public int[] Digest(int[] normalizedBundleFragment, int[] signatureFragment) - { - _curl.Reset(); - var buffer = new int[243]; - - for (var i = 0; i < 27; i++) - { - buffer = ArrayUtils.SubArray(signatureFragment, i * 243, (i + 1) * 243); - - var jCurl = new Kerl(); - - for (var j = normalizedBundleFragment[i] + 13; j-- > 0;) - { - jCurl.Reset(); - jCurl.Absorb(buffer); - jCurl.Squeeze(buffer); - } - - _curl.Absorb(buffer); - } - - _curl.Squeeze(buffer); - - return buffer; - } - - /// - /// - /// - /// - public int[] Address(int[] digests) - { - var address = new int[243]; - _curl.Reset(); - _curl.Absorb(digests, 0, digests.Length); - _curl.Squeeze(address, 0, address.Length); - return address; - } - - /// - /// - /// - /// - /// - public int[] SignatureFragment(int[] normalizedBundleFragment, int[] keyFragment) - { - var hash = new int[243]; - - for (var i = 0; i < 27; i++) - { - Array.Copy(keyFragment, i * 243, hash, 0, 243); - - for (var j = 0; j < 13 - normalizedBundleFragment[i]; j++) - { - _curl.Reset(); - _curl.Absorb(hash, 0, hash.Length); - _curl.Squeeze(hash, 0, hash.Length); - } - - - for (var j = 0; j < 243; j++) Array.Copy(hash, j, keyFragment, i * 243 + j, 1); - } - - return keyFragment; - } - - - /// - /// - /// - /// - /// - /// - public bool ValidateSignatures(Bundle signedBundle, string inputAddress) - { - string bundleHash = ""; - - List signatureFragments = new List(); - - foreach (var trx in signedBundle.Transactions) - { - if (trx.Address.Equals(inputAddress)) - { - bundleHash = trx.Bundle; - - // if we reached remainder bundle - String signatureFragment = trx.SignatureMessageFragment; - if (InputValidator.IsNinesTrytes(signatureFragment, signatureFragment.Length)) - { - break; - } - signatureFragments.Add(signatureFragment); - } - } - - return ValidateSignatures(inputAddress, signatureFragments.ToArray(), bundleHash); - } - - /// - /// - /// - /// - /// - /// - public bool ValidateSignatures(string expectedAddress, string[] signatureFragments, string bundleHash) - { - var bundle = new Bundle(); - - var normalizedBundleFragments = new int[3, 27]; - var normalizedBundleHash = bundle.NormalizedBundle(bundleHash); - - // Split hash into 3 fragments - for (var i = 0; i < 3; i++) - { - //normalizedBundleFragments[i] = Arrays.copyOfRange(normalizedBundleHash, i*27, (i + 1)*27); - //Array.Copy(normalizedBundleHash, i * 27, normalizedBundleFragments, 0, 27); - for (var j = 0; j < 27; j++) - { - normalizedBundleFragments[i, j] = normalizedBundleHash[i*27+j]; - } - } - - - // Get digests - var digests = new int[signatureFragments.Length * 243]; - - for (var i = 0; i < signatureFragments.Length; i++) - { - var digestBuffer = Digest(ArrayUtils.SliceRow(normalizedBundleFragments, i % 3).ToArray(), - Converter.ToTrits(signatureFragments[i])); - - Array.Copy(digestBuffer, 0, digests, i * 243, 243); - } - - var address = Converter.ToTrytes(Address(digests)); - - return expectedAddress.Equals(address); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/TritsConverter.cs b/IotaCSharpApi/Api/Utils/TritsConverter.cs deleted file mode 100644 index ba4ac2a..0000000 --- a/IotaCSharpApi/Api/Utils/TritsConverter.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// use sbyte for trits - /// - public class TritsConverter - { - /// - /// The maximum trit value - /// - public const sbyte MaxTritValue = 1; - - /// - /// The minimum trit value - /// - public const sbyte MinTritValue = -1; - - /// - /// The number of trits in a byte - /// - public const int NumberOfTritsInAByte = 5; - - /// - /// The number of trits in a tryte - /// - public const int NumberOfTritsInATryte = 3; - - private static readonly sbyte[][] ByteToTritsMappings = new sbyte[243][]; - private static readonly sbyte[][] TryteToTritsMappings = new sbyte[27][]; - - static TritsConverter() - { - var trits = new sbyte[NumberOfTritsInAByte]; - for (var i = 0; i < 243; i++) - { - ByteToTritsMappings[i] = new sbyte[NumberOfTritsInAByte]; - Array.Copy(trits, ByteToTritsMappings[i], NumberOfTritsInAByte); - Increment(trits, NumberOfTritsInAByte); - } - - trits = new sbyte[NumberOfTritsInATryte]; - for (var i = 0; i < 27; i++) - { - TryteToTritsMappings[i] = new sbyte[NumberOfTritsInATryte]; - Array.Copy(trits, TryteToTritsMappings[i], NumberOfTritsInATryte); - Increment(trits, NumberOfTritsInATryte); - } - } - - /// - /// - /// - /// - /// - /// - public static byte[] ToBytes(sbyte[] trits, int offset, int size) - { - var byteList = new List(); - - int index; - var endIndex = offset + size - 5; - int value; - - for (index = offset; index <= endIndex; index += 5) - { - value = trits[index] * 81 - + trits[index + 1] * 27 - + trits[index + 2] * 9 - + trits[index + 3] * 3 - + trits[index + 4]; - - byteList.Add((byte) value); - } - - // left - endIndex = offset + size; - if (index < endIndex) - { - value = 0; - while (index < endIndex) - { - value = value * 3 + trits[index]; - index++; - } - - byteList.Add((byte) value); - } - - return byteList.ToArray(); - } - - /// - /// - /// - /// - public static byte[] ToBytes(sbyte[] trits) - { - return ToBytes(trits, 0, trits.Length); - } - - /// - /// - /// - /// - /// - public static sbyte[] ToTrits(string trytes, int length) - { - var tritss = ToTrits(trytes); - - var tritsList = new List(tritss); - - while (tritsList.Count < length) - tritsList.Add(0); - - return tritsList.ToArray(); - } - - /// - /// - /// - /// - public static sbyte[] ToTrits(string trytes) - { - var tritss = new sbyte[3 * trytes.Length]; - for (var i = 0; i < trytes.Length; i++) - Array.Copy(TryteToTritsMappings[TrytesConverter.TryteToDecimal(trytes[i])], 0, - tritss, i * 3, 3); - - return tritss; - } - - - /// - /// - /// - /// - /// - public static sbyte[] ToTrits(long trytes, int length) - { - var tritss = ToTrits(trytes); - - var tritsList = new List(tritss); - - while (tritsList.Count < length) - tritsList.Add(0); - - return tritsList.ToArray(); - } - - /// - /// - /// - /// - public static sbyte[] ToTrits(long trytes) - { - if (trytes == 0) - return new sbyte[] {0}; - - var tritsList = new List(); - - while (trytes != 0) - { - var remainder = (sbyte) (trytes % 3); - trytes /= 3; - - if (remainder > MaxTritValue) - { - remainder = MinTritValue; - trytes += 1; - } - else if (remainder < MinTritValue) - { - remainder = MaxTritValue; - trytes -= 1; - } - - tritsList.Add(remainder); - } - - return tritsList.ToArray(); - } - - - /// - /// - /// - /// - /// - /// - public static string ToTrytes(sbyte[] trits, int offset, int size) - { - var trytes = new StringBuilder(); - var endIndex = offset + size - 3; - - for (var i = offset; i <= endIndex; i += 3) - { - var j = trits[i] + trits[i + 1] * 3 + trits[i + 2] * 9; - if (j < 0) j += Constants.TryteAlphabet.Length; - - trytes.Append(Constants.TryteAlphabet[j]); - } - - if (size % 3 != 0) - { - //TODO(gjc): need handle??? - } - - - return trytes.ToString(); - } - - /// - /// - /// - /// - public static string ToTrytes(sbyte[] trits) - { - return ToTrytes(trits, 0, trits.Length); - } - - /// - /// - /// - /// - public static int ToInt32(sbyte[] trits) - { - var value = 0; - - for (var i = trits.Length - 1; i >= 0; i--) value = value * 3 + trits[i]; - - return value; - } - - /// - /// - /// - /// - public static long ToInt64(sbyte[] trits) - { - long value = 0; - - for (var i = trits.Length - 1; i >= 0; i--) value = value * 3 + trits[i]; - - return value; - } - - #region Private Method - - private static void Increment(sbyte[] trits, int size) - { - for (var i = 0; i < size; i++) - if (++trits[i] > MaxTritValue) - trits[i] = MinTritValue; - else - break; - } - - #endregion - } -} \ No newline at end of file diff --git a/IotaCSharpApi/Api/Utils/TrytesConverter.cs b/IotaCSharpApi/Api/Utils/TrytesConverter.cs deleted file mode 100644 index 4121206..0000000 --- a/IotaCSharpApi/Api/Utils/TrytesConverter.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Text; - -namespace Iota.Lib.CSharp.Api.Utils -{ - /// - /// This class allows to convert between ASCII and tryte encoded strings - /// - public class TrytesConverter - { - /// - /// Converts the ASCII encoded string to trytes - /// - /// ASCII encoded string - /// tryte encoded string - public static string ToTrytes(string inputString) - { - var trytes = new StringBuilder(); - - foreach (var input in inputString) - { - var asciiValue = input; - - // If not recognizable ASCII character, replace with space - if (asciiValue > 255) asciiValue = ' '; - - trytes.Append(Constants.TryteAlphabet[asciiValue % 27]); - trytes.Append(Constants.TryteAlphabet[asciiValue / 27]); - } - - return trytes.ToString(); - } - - /// - /// Converts the specified tryte encoded String to ASCII - /// - /// tryte encoded string - /// an ASCII encoded string - public static string ToString(string inputTrytes) - { - var builder = new StringBuilder(); - - for (var i = 0; i < inputTrytes.Length; i += 2) - { - // get a trytes pair - - var firstValue = TryteToDecimal(inputTrytes[i]); - var secondValue = TryteToDecimal(inputTrytes[i + 1]); - var decimalValue = firstValue + secondValue * 27; - - builder.Append((char) decimalValue); - } - - return builder.ToString(); - } - - /// - /// Tryte To Decimal, '9' = 0 - /// - /// - /// - public static int TryteToDecimal(char tryte) - { - if (tryte == '9') - return 0; - - return tryte - 'A' + 1; - } - } -} \ No newline at end of file diff --git a/IotaCSharpApi/App.config b/IotaCSharpApi/App.config deleted file mode 100644 index 386b876..0000000 --- a/IotaCSharpApi/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/IotaCSharpApi/IotaCSharpApi.csproj b/IotaCSharpApi/IotaCSharpApi.csproj deleted file mode 100644 index b08830f..0000000 --- a/IotaCSharpApi/IotaCSharpApi.csproj +++ /dev/null @@ -1,160 +0,0 @@ - - - - - Debug - AnyCPU - {FC2C2F96-49EA-4046-95BD-3B570BDD1E13} - Library - Properties - Iota.Lib.CSharp - IotaApi - v4.6.1 - 512 - - - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - bin\Debug\IotaApi.xml - 7.2 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - bin\Release\IotaApi.xml - 7.2 - - - - - - - ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll - - - ..\packages\Iesi.Collections.4.0.4\lib\net461\Iesi.Collections.dll - - - ..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll - - - ..\packages\RestSharp.106.2.1\lib\net452\RestSharp.dll - - - - - ..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/IotaCSharpApi/IotaCSharpApi.csproj.DotSettings b/IotaCSharpApi/IotaCSharpApi.csproj.DotSettings deleted file mode 100644 index c54c126..0000000 --- a/IotaCSharpApi/IotaCSharpApi.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp70 \ No newline at end of file diff --git a/IotaCSharpApi/Package.nuspec b/IotaCSharpApi/Package.nuspec deleted file mode 100644 index f79c828..0000000 --- a/IotaCSharpApi/Package.nuspec +++ /dev/null @@ -1,24 +0,0 @@ - - - - Iota.Lib.CSharp - 0.9.0-beta - sniro - Iota Foundation & Contributors - https://github.com/iotaledger/iota.lib.charp/blob/master/LICENSE - https://github.com/iotaledger/iota.lib.charp - false - Iota C# Library - First Beta Version - Copyright 2017 - Iota - - - - - - - - - - \ No newline at end of file diff --git a/IotaCSharpApi/Properties/AssemblyInfo.cs b/IotaCSharpApi/Properties/AssemblyInfo.cs deleted file mode 100644 index 37172ad..0000000 --- a/IotaCSharpApi/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("Iota.Lib.CSharp")] -[assembly: AssemblyDescription("Iota Library")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Iota Foundation")] -[assembly: AssemblyProduct("Iota.Lib.CSharp")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("fc2c2f96-49ea-4046-95bd-3b570bdd1e13")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("0.9.0.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/IotaCSharpApi/packages.config b/IotaCSharpApi/packages.config deleted file mode 100644 index dffe88e..0000000 --- a/IotaCSharpApi/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/IotaApiTests.cs b/IotaCSharpApiUnitTests/Api/IotaApiTests.cs deleted file mode 100644 index fb89e00..0000000 --- a/IotaCSharpApiUnitTests/Api/IotaApiTests.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System; -using System.Collections.Generic; -using Iota.Lib.CSharp.Api; -using Iota.Lib.CSharp.Api.Exception; -using Iota.Lib.CSharp.Api.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api -{ - [TestClass] - public class IotaApiTests - { - private static readonly string TEST_SEED1 = - "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_1 = - "MALAZGDVZIAQQRTNYJDSZMY9VE9LAHQKTVCUOAGZUCX9IBUMODFFTMGUIUAXGLWZQ9CYRSLYBM9QBIBYAEIAOPKXEA"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2 = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_3 = - "ASCZZOBQDMNHLELQKWJBMRETMHBTF9V9TNKYDIFW9PDXPUHPVVGHMSWPVMNJHSJF99QFCMNTPCPGS9DT9XAFKJVO9X"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_1 = - "MALAZGDVZIAQQRTNYJDSZMY9VE9LAHQKTVCUOAGZUCX9IBUMODFFTMGUIUAXGLWZQ9CYRSLYBM9QBIBYA"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2 = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZC"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_3 = - "ASCZZOBQDMNHLELQKWJBMRETMHBTF9V9TNKYDIFW9PDXPUHPVVGHMSWPVMNJHSJF99QFCMNTPCPGS9DT9"; - - private static readonly string TEST_SEED2 = - "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - - private static readonly string TEST_HASH = - "9XWWWXVQYPKLVMAMFPXFSE9UCAGVY9RZO9NHGAZEXIRIJRZULGMFOJNDKUNFUCSURWRDDPVMYG9X99999"; //06/02/2018,04:36 - - private static readonly string TEST_TRYTES = - "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCQ9CA9TA99999999999999999999XC9999999999999999999999999TPLCUYD99999999999B99999999WFVTNIRLVFIVKAQEEDFRWWLXIPHRQNG9EAY9QEWRFDLECXDGJLIKBAKBYPTAZPISWVXJLBJISGGLWTBVDNBEBXBG9PZHPK9SVNH99LZVXYZSVODZZIIXNJJQAYXCNKISVFVXGVQMURVEMSDGLRLZADQCOHRHW99999K9SYJTSNRZVYWGSV9AXVTPKMTLHPCTIJGNNAMALVPQUCGCZXZFFUQSXCHPSJLXBADVOIZO9PSZYTA9999XC9999999999999999999999999WWF9MWCJE999999999MMMMMMMMMPRQAGSHN9ZHEWVAANNPXSDRRROY"; - - private static readonly string TEST_MESSAGE = "COTA"; - private static readonly string TEST_TAG = "COTASPAM9999999999999999999"; - - // ReSharper disable once InconsistentNaming - private static readonly string[] TEST_ADDRESSES = - { - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC", - "P9UDUZMN9DEXCRQEKLJYSBSBZFCHOBPJSDKMLCCVJDOVOFDWMNBZRIRRZJGINOUMPJBMYYZEGRTIDUABD", - "MIMVJEYREIIZLXOXQROMPJFCIX9NFVXD9ZQMNZERPI9OJIJFUWQ9WCTMKXEEPHYPWEZPHLJBJOFH9YTRB", - "FOJHXRVJRFMJTFDUWJYYZXCZIJXKQALLXMLKHZFDMHWTIBBXUKSNSUYJLKYRQBNXKRSUXZHDTPWXYD9YF", - "B9YNPQO9EXID9RDEEGLCBJBYKBLWHTOQOZKTLJDFPJZOPKJJTNUYUVVTDJPBCBYIWGPSCMNRZFGFHFSXH", - "NQEFOAFIYKZOUXDFQ9X9PHCNSDETRTJZINZ9EYGKU99QJLDSTSC9VTBAA9FHLNLNYQXWLTNPRJDWCGIPP", - "CEGLBSXDJVXGKGOUHRGMAQDRVYXCQLXBKUDWKFFSIABCUYRATFPTEEDIFYGAASKFZYREHLBIXBTKP9KLC", - "QLOXU9GIQXPPE9UUT9DSIDSIESRIXMTGZJMKLSJTNBCRELAVLWVJLUOLKGFCWAEPEQWZWPBV9YZJJEHUS", - "XIRMYJSGQXMM9YPHJVVLAVGBBLEEMOOKHHBFWKEAXJFONZLNSLBCGPQEVDMMOGHFVRDSYTETIFOIVNCR9", - "PDVVBYBXMHZKADPAYOKQNDPHRSWTHAWQ9GRVIBOIMZQTYCWEPCDWDVRSOUNASVBDLBOAMVLYEVVCMAM9N", - "U9GAIAPUUQWJGISAZWPLHUELTZ9WSHWXS9JLPKOWHRRIVUKGWCTJMBULVMKTETTUNHZ9HWHBALUCJIROU", - "VFPMKZLLMDUOEKNBEKQZPTNZJZF9UHRWSTHXLWQQ9OAXTZQHTZPAWNJNXKAZFSDFWKFQEKZIGJTLWQFLO", - "IGHK9XIWOAYBZUEZHQLEXBPTXSWVANIOUZZCPNKUIJIJOJNAQCJWUJHYKCZOIKVAAHDGAWJZKLTPVQL9G", - "LXQPWMNXSUZTEYNC9ZBBFHY9YWCCOVKBNIIOUSVXZJZMJKJFDUWGUVXYCHGKUHEEIDHSGEWFAHVJPRIJT", - "AKFDX9PGGQLZUWRMZ9YBDF9CG9TWXCNALCSXSAWHFIMGXCSYCJLSWIQDGGVDRMNEKKECQEYAITGNLNJFQ", - "YX9QSPYMSFVOW9UVZRDVOCPYYMUTDHCCPKHMXQSJQJYIXVCHILKW9GBYJTYGLIKBTRQMDCYBMLLNGSSIK", - "DSYCJKNG9TAGJHSKZQ9XLKAKNSKJFZIPVEDGJFXRTFGENHZFQGXHWDBNXLLDABDMOYELPG9DIXSNJFWAR", - "9ANNACZYLDDPZILLQBQG9YMG9XJUMTAENDFQ9HMSSEFWYOAXPJTUXBFTSAXDJPAO9FKTWBBSCSFMOUR9I", - "WDTFFXHBHMFQQVXQLBFJFVVHVIIAVYM9PFAZCHMKET9ESMHIRHSMVDJBZTXPTAFVIASMSXRDCIYVWVQNO", - "XCCPS9GMTSUB9DXPVKLTBDHOFX9PJMBYZQYQEXMRQDPGQPLWRGZGXODYJKGVFOHHYUJRCSXAIDGYSAWRB", - "KVEBCGMEOPDPRCQBPIEMZTTXYBURGZVNH9PLHKPMM9D9FUKWIGLKZROGNSYIFHULLWQWXCNAW9HKKVIDC" - }; - - private static readonly string TestTrytesValid = - "JUSTANOTHERTEST999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTH999999999999999999999999999COTASPAM9999999999999999999VISYF9DGE999999999999999999FB9CRHGOHK9EIDHDUWSGDDONYQAABTRXXMFUKRZHMVJAPCAADTRDCWZJRHAPL9LRIVZFVKQV9GAWSSJZDPWGPQTPWCPNYONYGGSJLJAQYXLZ9FMOTUJT9RIXAOXFDQZSTZYBCHSNLSM9JAXTMNQBUHAAZIIR999999PWGPQTPWCPNYONYGGSJLJAQYXLZ9FMOTUJT9RIXAOXFDQZSTZYBCHSNLSM9JAXTMNQBUHAAZIIR999999KXOQZNGXOCACOVYKPWWJFQQMEWDQVUZRI99WFQEJANSOPVLZGQHLUEYKPYPMSTLDRDVEBMCQMKQLL9JFS"; - - private static int MIN_WEIGHT_MAGNITUDE = 14; - private static int DEPTH = 9; - - private IotaApi _iotaClient; - - [TestInitialize] - public void CreateApiClientInstance() - { - _iotaClient = new IotaApi("node.iotawallet.info", 14265); - } - - [TestMethod] - public void ShouldGetInputs() - { - var res = _iotaClient.GetInputs(TEST_SEED1, 2, 0, 0, 0); - Console.WriteLine(res); - Assert.IsNotNull(res); - Assert.IsNotNull(res.TotalBalance); - Assert.IsNotNull(res.InputsList); - } - - [TestMethod] - public void ShouldCreateANewAddressWithChecksum() - { - // ReSharper disable RedundantArgumentDefaultValue - var res1 = _iotaClient.GetNewAddress(TEST_SEED1, 1, 0, true, 5, false); - Assert.AreEqual(res1[0], TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_1); - - var res2 = _iotaClient.GetNewAddress(TEST_SEED1, 2, 0, true, 5, false); - Assert.AreEqual(res2[0], TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2); - - var res3 = _iotaClient.GetNewAddress(TEST_SEED1, 3, 0, true, 5, false); - Assert.AreEqual(res3[0], TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_3); - // ReSharper restore RedundantArgumentDefaultValue - } - - [TestMethod] - public void ShouldCreateANewAddressWithoutChecksum() - { - // ReSharper disable RedundantArgumentDefaultValue - var res1 = _iotaClient.GetNewAddress(TEST_SEED1, 1, 0, false, 5, false); - Assert.AreEqual(res1[0], TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_1); - - var res2 = _iotaClient.GetNewAddress(TEST_SEED1, 2, 0, false, 5, false); - Assert.AreEqual(res2[0], TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2); - - var res3 = _iotaClient.GetNewAddress(TEST_SEED1, 3, 0, false, 5, false); - Assert.AreEqual(res3[0], TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_3); - // ReSharper restore RedundantArgumentDefaultValue - } - - [TestMethod] - public void ShouldCreate100Addresses() - { - // ReSharper disable RedundantArgumentDefaultValue - var res = _iotaClient.GetNewAddress(TEST_SEED1, 2, 0, false, 100, false); - Assert.AreEqual(res.Length, 100); - // ReSharper restore RedundantArgumentDefaultValue - } - - [TestMethod] - [ExpectedException(typeof(NotEnoughBalanceException))] - public void ShouldPrepareTransfer() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 100, TEST_MESSAGE, TEST_TAG), - }; - - var trytes = _iotaClient.PrepareTransfers(TEST_SEED1, 2, transfers.ToArray(), null, null, false); - - Assert.IsNotNull(trytes); - Assert.IsFalse(trytes.Count == 0); - - } - - //seed contains 0 balance - [TestMethod] - [ExpectedException(typeof(NotEnoughBalanceException))] - public void ShouldPrepareTransferWithInputs() - { - List inputlist = new List(); - List transfers = new List(); - - var inputs = _iotaClient.GetInputs(TEST_SEED1, 2, 0, 0, 0); - - inputlist.AddRange(inputs.InputsList); - - transfers.Add(new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 100, TEST_MESSAGE, TEST_TAG)); - List trytes = - _iotaClient.PrepareTransfers(TEST_SEED1, 2, transfers.ToArray(), null, inputlist, true); - - Assert.IsNotNull(trytes); - Assert.IsFalse(trytes.Count == 0); - } - - - [TestMethod] - public void ShouldGetLastInclusionState() - { - var res = _iotaClient.GetLatestInclusion(new[] {TEST_HASH}); - Assert.IsNotNull(res.States); - } - - [TestMethod] - public void ShouldFindTransactionObjects() - { - var ftr = _iotaClient.FindTransactionObjects(TEST_ADDRESSES); - Assert.IsNotNull(ftr); - } - - [TestMethod] - public void ShouldGetAccountData() - { - var accountData = _iotaClient.GetAccountData(TEST_SEED1, 2, 0, true, 0, true, 0, 0, true, 0); - Assert.IsNotNull(accountData); - } - - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void ShouldNotGetBundle() - { - var bundle = _iotaClient.GetBundle("SADASD"); - Assert.IsNotNull(bundle); - } - - [TestMethod] - public void ShouldGetBundle() - { - var bundle = _iotaClient.GetBundle(TEST_HASH); - Assert.IsNotNull(bundle); - } - - [TestMethod] - public void ShouldGetTransfers() - { - // ReSharper disable RedundantArgumentDefaultValue - var gtr = _iotaClient.GetTransfers(TEST_SEED1, 2, 0, 0, false); - // ReSharper restore RedundantArgumentDefaultValue - - foreach (var b in gtr) Assert.IsTrue(b.Transactions.TrueForAll(t => t != null)); - } - - [Ignore] - [TestMethod] - public void ShouldReplayBundle() - { - var replayedList = _iotaClient.ReplayBundle(TEST_HASH, DEPTH, MIN_WEIGHT_MAGNITUDE); - Assert.IsNotNull(replayedList); - } - - [Ignore] - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void ShouldNotSendTrytes() - { - _iotaClient.SendTrytes(new[] {TEST_TRYTES}, 9); - } - - [TestMethod] - public void ShouldGetTrytes() - { - _iotaClient.GetTrytes(TEST_HASH); - } - - [TestMethod] - public void ShouldBroadcastAndStore() - { - _iotaClient.BroadcastAndStore(new List {TEST_TRYTES}); - } - - [Ignore] - [TestMethod] - public void ShouldSendTrytes() - { - _iotaClient.SendTrytes(new[] {TestTrytesValid}, 9); - } - - [Ignore] - [TestMethod] - [ExpectedException(typeof(IllegalStateException))] - public void ShouldNotSendTransfer() - { - Transfer[] transfers = - { - new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 2, TEST_MESSAGE, TEST_TAG) - }; - - var result = _iotaClient.SendTransfer(TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers, null, null, - false, true); - Assert.IsNotNull(result); - } - - [Ignore] - [TestMethod] - public void ShouldSendTransferWithoutInputs() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 1, "JUSTANOTHERTEST", TEST_TAG) - }; - - var str = _iotaClient.SendTransfer(TEST_SEED2, 2, 9, 14, transfers.ToArray(), null, null, false, true); - - Assert.IsNotNull(str); - } - - [Ignore] - [TestMethod] - public void ShouldSendTransferWithInputs() - { - List inputlist = new List(); - List transfers = new List(); - - var inputs = _iotaClient.GetInputs(TEST_SEED1, 2, 0, 0, 1); - - inputlist.AddRange(inputs.InputsList); - - transfers.Add(new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 1, TEST_MESSAGE, TEST_TAG)); - - var str = _iotaClient.SendTransfer(TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers.ToArray(), - inputlist.ToArray(), null, - true, true); - - Assert.IsNotNull(str); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/IotaCoreApiTest.cs b/IotaCSharpApiUnitTests/Api/IotaCoreApiTest.cs deleted file mode 100644 index dcfa172..0000000 --- a/IotaCSharpApiUnitTests/Api/IotaCoreApiTest.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Linq; -using Iota.Lib.CSharp.Api; -using Iota.Lib.CSharp.Api.Exception; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api -{ - [TestClass] - public class IotaCoreApiTest - { - private static readonly string TEST_BUNDLE = - "XZKJUUMQOYUQFKMWQZNTFMSS9FKJLOEV9DXXXWPMQRTNCOUSUQNTBIJTVORLOQPLYZOTMLFRHYKMTGZZU"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM = - "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA"; - - private static readonly string TEST_HASH = - "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999"; - - private static IotaApi _iotaApi; - - [TestInitialize] - public void CreateProxyInstance() - { - _iotaApi = new IotaApi("node.iotawallet.info", 14265); - } - - [TestMethod] - public void ShouldGetNodeInfo() - { - var nodeInfo = _iotaApi.GetNodeInfo(); - Assert.IsNotNull(nodeInfo.AppVersion); - Assert.IsNotNull(nodeInfo.AppName); - Assert.IsNotNull(nodeInfo.JreVersion); - Assert.IsNotNull(nodeInfo.JreAvailableProcessors); - Assert.IsNotNull(nodeInfo.JreFreeMemory); - Assert.IsNotNull(nodeInfo.JreMaxMemory); - Assert.IsNotNull(nodeInfo.JreTotalMemory); - Assert.IsNotNull(nodeInfo.LatestMilestone); - Assert.IsNotNull(nodeInfo.LatestMilestoneIndex); - Assert.IsNotNull(nodeInfo.LatestSolidSubtangleMilestone); - Assert.IsNotNull(nodeInfo.LatestSolidSubtangleMilestoneIndex); - Assert.IsNotNull(nodeInfo.Neighbors); - Assert.IsNotNull(nodeInfo.PacketsQueueSize); - Assert.IsNotNull(nodeInfo.Time); - Assert.IsNotNull(nodeInfo.Tips); - Assert.IsNotNull(nodeInfo.TransactionsToRequest); - } - - [TestMethod] - public void ShouldGetNeighbors() - { - var neighbors = _iotaApi.GetNeighbors(); - Assert.IsNotNull(neighbors.Neighbors); - } - - [TestMethod] - public void ShouldAddNeighbors() - { - try - { - var res = _iotaApi.AddNeighbors("udp://8.8.8.8:14265"); - Assert.IsNotNull(res); - } - catch (IotaApiException e) - { - Assert.IsTrue(e.Message.Contains("not available on this node")); - } - } - - [TestMethod] - public void ShouldRemoveNeighbors() - { - try - { - var res = _iotaApi.RemoveNeighbors("udp://8.8.8.8:14265"); - Assert.IsNotNull(res); - } - catch (IotaApiException e) - { - Assert.IsTrue(e.Message.Contains("not available on this node")); - } - } - - [TestMethod] - public void ShouldGetTips() - { - var tips = _iotaApi.GetTips(); - Assert.IsNotNull(tips); - } - - [TestMethod] - public void ShouldFindTransactionsByAddresses() - { - var trans = _iotaApi.FindTransactionsByAddresses(TEST_ADDRESS_WITH_CHECKSUM); - Assert.IsNotNull(trans.Hashes); - Assert.IsTrue(trans.Hashes.Count > 0); - } - - [TestMethod] - public void ShouldFindTransactionsByApprovees() - { - var trans = _iotaApi.FindTransactionsByApprovees(TEST_HASH); - Assert.IsNotNull(trans.Hashes); - } - - [TestMethod] - public void ShouldFindTransactionsByBundles() - { - var trans = _iotaApi.FindTransactionsByBundles(TEST_HASH); - Assert.IsNotNull(trans.Hashes); - } - - [TestMethod] - public void ShouldFindTransactionsByDigests() - { - var trans = _iotaApi.FindTransactionsByDigests(TEST_HASH); - Assert.IsNotNull(trans.Hashes); - } - - [TestMethod] - public void ShouldGetTrytes() - { - var res = _iotaApi.GetTrytes(TEST_HASH); - Assert.IsNotNull(res.Trytes); - } - - [TestMethod] - [ExpectedException(typeof(IotaApiException), "One of the tips absents")] - public void ShouldNotGetInclusionStates() - { - var res = _iotaApi.GetInclusionStates(new[] {TEST_HASH}, - new[] {"DNSBRJWNOVUCQPILOQIFDKBFJMVOTGHLIMLLRXOHFTJZGRHJUEDAOWXQRYGDI9KHYFGYDWQJZKX999999"}); - Assert.IsNotNull(res.States); - } - - [TestMethod] - public void ShouldGetInclusionStates() - { - var res = - _iotaApi.GetInclusionStates( - new[] {TEST_HASH}, - new[] {_iotaApi.GetNodeInfo().LatestSolidSubtangleMilestone}); - Assert.IsNotNull(res.States); - } - - [TestMethod] // very long execution - public void ShouldGetTransactionsToApprove() - { - var res = _iotaApi.GetTransactionsToApprove(27); - Assert.IsNotNull(res.TrunkTransaction); - Assert.IsNotNull(res.BranchTransaction); - } - - [TestMethod] - public void ShouldFindTransactions() - { - var test = TEST_BUNDLE; - // ReSharper disable once UnusedVariable - var resp = _iotaApi.FindTransactions(new[] {test}.ToList(), - new[] {test}.ToList(), new[] {test}.ToList(), new[] {test}.ToList()); - } - - [TestMethod] - public void ShouldGetBalances() - { - var res = _iotaApi.GetBalances(new[] {TEST_ADDRESS_WITH_CHECKSUM}.ToList(), 100); - Assert.IsNotNull(res.Balances); - Assert.IsNotNull(res.References); - Assert.IsNotNull(res.MilestoneIndex); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/Pow/KerlTest.cs b/IotaCSharpApiUnitTests/Api/Pow/KerlTest.cs deleted file mode 100644 index 6c19363..0000000 --- a/IotaCSharpApiUnitTests/Api/Pow/KerlTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Iota.Lib.CSharp.Api.Pow; -using Iota.Lib.CSharp.Api.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api.Pow -{ - [TestClass] - public class KerlTest - { - [TestMethod] - public void ShouldCreateValidHash1() - { - var trits = Converter.ToTrits( - "GYOMKVTSNHVJNCNFBBAH9AAMXLPLLLROQY99QN9DLSJUHDPBLCFFAIQXZA9BKMBJCYSFHFPXAHDWZFEIZ"); - var kerl = new Kerl(); - kerl.Reset(); - kerl.Absorb(trits, 0, trits.Length); - var hashTrits = new int[trits.Length]; - kerl.Squeeze(hashTrits, 0, 243); - var hash = Converter.ToTrytes(hashTrits); - Assert.AreEqual(hash, "OXJCNFHUNAHWDLKKPELTBFUCVW9KLXKOGWERKTJXQMXTKFKNWNNXYD9DMJJABSEIONOSJTTEVKVDQEWTW"); - } - - [TestMethod] - public void ShouldCreateValidHash2() - { - var trits = Converter.ToTrits( - "9MIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJFGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH"); - var kerl = new Kerl(); - kerl.Reset(); - kerl.Absorb(trits, 0, trits.Length); - var hashTrits = new int[trits.Length * 2]; - kerl.Squeeze(hashTrits, 0, 243 * 2); - var hash = Converter.ToTrytes(hashTrits); - Assert.AreEqual(hash, - "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA"); - } - - [TestMethod] - public void ShouldCreateValidHash3() - { - var trits = Converter.ToTrits( - "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA"); - var kerl = new Kerl(); - kerl.Reset(); - kerl.Absorb(trits, 0, trits.Length); - var hashTrits = new int[trits.Length]; - kerl.Squeeze(hashTrits, 0, 243 * 2); - var hash = Converter.ToTrytes(hashTrits); - Assert.AreEqual(hash, - "LUCKQVACOGBFYSPPVSSOXJEKNSQQRQKPZC9NXFSMQNRQCGGUL9OHVVKBDSKEQEBKXRNUJSRXYVHJTXBPDWQGNSCDCBAIRHAQCOWZEBSNHIJIGPZQITIBJQ9LNTDIBTCQ9EUWKHFLGFUVGGUWJONK9GBCDUIMAYMMQX"); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/Pow/LocalPoWTest.cs b/IotaCSharpApiUnitTests/Api/Pow/LocalPoWTest.cs deleted file mode 100644 index eebc1a6..0000000 --- a/IotaCSharpApiUnitTests/Api/Pow/LocalPoWTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using Iota.Lib.CSharp.Api; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Pow; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api.Pow -{ - [TestClass] - public class LocalPoWTest - { - private static readonly string TEST_SEED1 = - "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2 = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZC"; - - private static readonly string TEST_MESSAGE = "JUSTANOTHERJOTATEST"; - private static readonly string TEST_TAG = "JOTASPAM9999999999999999999"; - private static readonly int MIN_WEIGHT_MAGNITUDE = 14; - private static readonly int DEPTH = 9; - - private IotaApi _iotaClient; - - [TestInitialize] - public void Setup() - { - _iotaClient = new IotaApi("node.iotawallet.info", 14265) - { - LocalPow = new PearlDiverLocalPoW() - }; - } - - [TestMethod] - public void ShouldSendTransfer() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, 0, TEST_MESSAGE, TEST_TAG) - }; - var result = _iotaClient.SendTransfer( - TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers.ToArray(), - null, null, false, false); - Assert.IsNotNull(result); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/Pow/PearlDiverTest.cs b/IotaCSharpApiUnitTests/Api/Pow/PearlDiverTest.cs deleted file mode 100644 index c2130dd..0000000 --- a/IotaCSharpApiUnitTests/Api/Pow/PearlDiverTest.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Text; -using Iota.Lib.CSharp.Api.Pow; -using Iota.Lib.CSharp.Api.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api.Pow -{ - [TestClass] - public class PearlDiverTest - { - - private const int TryteLength = 2673; - private const int MinWeightMagnitude = 9; - private const int NumCores = -1; // use n-1 cores - - private static readonly Random Random = new Random(); - private PearlDiver _pearlDiver; - private int[] _hashTrits; - - [TestInitialize] - public void Setup() - { - _pearlDiver = new PearlDiver(); - _hashTrits = new int[Sponge.HashLength]; - } - - - [TestMethod] - public void TestRandomTryteHash() - { - string testTrytes = GetRandomTrytes(); - - string hash = GetHashFor(testTrytes); - - string subHash = hash.Substring(Sponge.HashLength / 3 - MinWeightMagnitude / 3); - - bool success = InputValidator.IsNinesTrytes(subHash,subHash.Length); - if (!success) - { - Console.WriteLine(testTrytes); - } - - Assert.IsTrue(success, "The hash should have n nines"); - } - - [TestMethod] - [Ignore] - public void TestRandomTryteHash100() - { - for (int i = 0; i < 100; i++) - { - string testTrytes = GetRandomTrytes(); - - string hash = GetHashFor(testTrytes); - - string subHash = hash.Substring(Sponge.HashLength / 3 - MinWeightMagnitude / 3); - - bool success = InputValidator.IsNinesTrytes(subHash, subHash.Length); - if (!success) - { - Console.WriteLine(testTrytes); - } - - Assert.IsTrue(success, "The hash should have n nines"); - } - } - - private string GetRandomTrytes() - { - var trytes = new StringBuilder(); - - for (int i = 0; i < TryteLength; i++) - { - trytes.Append(Constants.TryteAlphabet[Random.Next(27)]); - } - - return trytes.ToString(); - } - - private string GetHashFor(string trytes) - { - Sponge curl = new Curl(CurlMode.CurlP81); - int[] myTrits = Converter.ToTrits(trytes); - - bool result = _pearlDiver.Search(myTrits, MinWeightMagnitude, NumCores); - - Assert.IsTrue(result,"Search Failed"); - - curl.Absorb(myTrits, 0, myTrits.Length); - curl.Squeeze(_hashTrits, 0, Sponge.HashLength); - curl.Reset(); - - return Converter.ToTrytes(_hashTrits); - } - } -} diff --git a/IotaCSharpApiUnitTests/Api/Utils/BigIntConverterTest.cs b/IotaCSharpApiUnitTests/Api/Utils/BigIntConverterTest.cs deleted file mode 100644 index bfd1fae..0000000 --- a/IotaCSharpApiUnitTests/Api/Utils/BigIntConverterTest.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using Iota.Lib.CSharp.Api.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Org.BouncyCastle.Math; - -namespace Iota.Lib.CSharpTests.Api.Utils -{ - [TestClass] - public class BigIntConverterTest - { - private static readonly Random Random = new Random(); - // trits<->BigInteger<->byte - - [TestMethod] - public void TestTritsAndBigInt() - { - int[] inputTrits = new int[243]; - for (int i = 0; i < inputTrits.Length; i++) - { - inputTrits[i] = Random.Next(3) - 1; - } - - var bigInt = BigIntConverter.BigIntFromTrits(inputTrits, 0, inputTrits.Length); - - int[] outputTrits = new int[inputTrits.Length]; - BigIntConverter.TritsFromBigInt(bigInt, outputTrits, 0, outputTrits.Length); - - for (int i = 0; i < inputTrits.Length; i++) - { - Assert.AreEqual(inputTrits[i], outputTrits[i]); - } - } - - [TestMethod] - public void TestBigIntAndByte() - { - byte[] bytes = new byte[48]; - BigInteger bigInt0 = new BigInteger("-123456"); - - BigIntConverter.BytesFromBigInt(bigInt0, bytes, 0, bytes.Length); - var bigInt1 = BigIntConverter.BigIntFromBytes(bytes, 0, bytes.Length); - - Assert.AreEqual(bigInt0,bigInt1); - } - - [TestMethod] - public void TestFixedBigInt() - { - int[] inputTrits = new int[243]; - int[] outputTrits = new int[243]; - byte[] bytes = new byte[384/8]; - - for (int i = 0; i < inputTrits.Length; i++) - { - inputTrits[i] = Random.Next(3) - 1; - } - - inputTrits[inputTrits.Length - 1] = 0; - FixedBigIntConverter.FromTritsToBytes(inputTrits,bytes); - FixedBigIntConverter.FromBytesToTrits(bytes,outputTrits); - outputTrits[outputTrits.Length - 1] = 0; - - for (int i = 0; i < inputTrits.Length; i++) - { - Assert.AreEqual(inputTrits[i], outputTrits[i]); - } - } - } -} diff --git a/IotaCSharpApiUnitTests/Api/Utils/InputValidatorTests.cs b/IotaCSharpApiUnitTests/Api/Utils/InputValidatorTests.cs deleted file mode 100644 index f75f843..0000000 --- a/IotaCSharpApiUnitTests/Api/Utils/InputValidatorTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Collections.Generic; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api.Utils -{ - [TestClass] - public class InputValidatorTests - { - private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM = - "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA"; - - private static readonly string TEST_ADDRESS_WITH_CHECKSUM = - "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA"; - - private static readonly string TEST_TRYTES = - "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"; - - private static readonly string TEST_HASH = - "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999"; - - private static readonly string TEST_MESSAGE = "JOTA"; - private static readonly string TEST_TAG = "JOTASPAM9999999999999999999"; - - [TestMethod] - public void ShouldIsAddress() - { - Assert.AreEqual(InputValidator.IsAddress(TEST_ADDRESS_WITHOUT_CHECKSUM), true); - } - - [TestMethod] - public void ShouldCheckAddress() - { - Assert.AreEqual(InputValidator.IsAddress(TEST_ADDRESS_WITHOUT_CHECKSUM), true); - } - - [TestMethod] - public void ShouldIsTrytes() - { - Assert.AreEqual(InputValidator.IsTrytes(TEST_TRYTES, TEST_TRYTES.Length), true); - } - - [TestMethod] - public void ShouldIsValue() - { - Assert.AreEqual(InputValidator.IsValue("1234"), true); - } - - [TestMethod] - public void IsValueNeg() - { - Assert.AreEqual(InputValidator.IsValue("-1234"), true); - } - - [TestMethod] - public void IsValueNeg2() - { - Assert.AreEqual(InputValidator.IsValue("-"), false); - } - - [TestMethod] - public void ShouldIsArrayOfHashes() - { - Assert.AreEqual(InputValidator.IsArrayOfHashes(new[] {TEST_HASH, TEST_HASH}), true); - } - - [TestMethod] - public void ShouldIsArrayOfTrytes() - { - Assert.AreEqual(InputValidator.IsArrayOfTrytes(new [] { TEST_TRYTES, TEST_TRYTES },2673), true); - } - - [TestMethod] - public void ShouldIsNinesTrytes() - { - Assert.AreEqual(InputValidator.IsNinesTrytes("999999999", 9), true); - } - - - [TestMethod] - public void ShouldIsTransfersCollectionCorrect() - { - var transfers = new List - { - new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG), - new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG) - }; - Assert.AreEqual(InputValidator.IsTransfersCollectionValid(transfers), true); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/Utils/IotaUnitConverterTests.cs b/IotaCSharpApiUnitTests/Api/Utils/IotaUnitConverterTests.cs deleted file mode 100644 index 0cf61d8..0000000 --- a/IotaCSharpApiUnitTests/Api/Utils/IotaUnitConverterTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Iota.Lib.CSharp.Api.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api.Utils -{ - [TestClass] - public class IotaUnitConverterTest - { - [TestMethod] - public void ShouldConvertUnitItoKi() - { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Iota, IotaUnits.Kilo), 1); - } - - [TestMethod] - public void ShouldConvertUnitKiToMi() - { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Kilo, IotaUnits.Mega), 1); - } - - [TestMethod] - public void ShouldConvertUnitMiToGi() - { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Mega, IotaUnits.Giga), 1); - } - - [TestMethod] - public void ShouldConvertUnitGiToTi() - { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Giga, IotaUnits.Terra), 1); - } - - [TestMethod] - public void ShouldConvertUnitTiToPi() - { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Terra, IotaUnits.Peta), 1); - } - - [TestMethod] - public void ShouldFindOptimizeUnitToDisplay() - { - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1), IotaUnits.Iota); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000), IotaUnits.Kilo); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000), IotaUnits.Mega); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000), IotaUnits.Giga); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000000L), IotaUnits.Terra); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000000000L), IotaUnits.Peta); - } - - /*[TestMethod] - public void shouldConvertRawIotaAmountToDisplayText() - { - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1, false), "1 i"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000, false), "1 Ki"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000, false), "1 Mi"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000, false), "1 Gi"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000L, false), "1 Ti"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000000L, false), "1 Pi"); - }*/ - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/Utils/SigningTest.cs b/IotaCSharpApiUnitTests/Api/Utils/SigningTest.cs deleted file mode 100644 index 26c93d8..0000000 --- a/IotaCSharpApiUnitTests/Api/Utils/SigningTest.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Pow; -using Iota.Lib.CSharp.Api.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api.Utils -{ - [TestClass] - public class SigningTest - { - private static readonly string TEST_SEED = - "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - - private static readonly string FIRST_ADDR = - "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC"; - - private static readonly string SIXTH_ADDR = - "HLHRSJNPUUGRYOVYPSTEQJKETXNXDIWQURLTYDBJADGIYZCFXZTTFSOCECPPPPY9BYWPODZOCWJKXEWXDPUYEOTFQA"; - - private static readonly string SIGNATURE1 = - "PYWFM9MYTPNZ9HTLZBBB9CGQWKPALDUNAQYCAA9VMQ9UMBLLAXSPPHQSNAAKJA9MZBXBHBQBFFKMBSDHDTCVCDWLUYCEQ9YZJAJAXXXZHDWTSLWGIWRE9LJFVWAFUMOAGHDBHJQ9APNBLSX9GPTJNTO9SBJT9UKYCZXYAWVGXEBJANNWEWZSPRYHASHGIFUWOEHUFMP9MWQBYZOZESCPLVJUCWGLEJIDPMEVNPBITBNFSQ9GBWCDTQZOPLPXOWWNQAEIXQRWMHAQDH9C9KKHGNKAX9INMUVVGIK9TPGRHOMDFAB9VICYDMSHHDDBRSTEFSZXMXFJUQRRAFBSCNHSMKRNNTTCMBURKBGC9EDWKLPBSQAKYCUKKSZWRVURZGUA9QVSXXPICIYFHLPJSWEFBZPUTWWNIKSAJM9OMRFFQVFJZZHLQBSEYXM9CN9HCGHSJBTYDGWOQPXOPZZE9EPQAQFT9GDWZCSOPMZHYYZXDDZ9DJDLOOOTIFQANFANNAYVIRUNDXSB9XRNXJYRDBLTEDWSUOVISMCHGKD9KDRSFDWRSVZQQKGAMDXFAWBSLMTTUMH9RAUIVI9HJMTODACSOP9MLHOJMSIWQ9TTNGPXRNWRHLMEMAH9GZHJRNJHQNBBLWKFXIZBMGMATZIZBFDPAFDCLDIFFAIK9JUSFYYC9ANDGXCZFLZYGURTUI9SWYYRGDJAHXDDNHSJZBCENZUSQXSFZMTXSFLRK9RIYAUMHPBOBNOXCHDIMBGIBVOOHIDQ9ORHHDECDTREIEILWDUFMUWYMGIXBIKRZMKGXTYZTX9GKFP9AUXMTUUQXRHHKPYULGJFJLEEYCNKLOWULRIAFM9OYKEDFRXFVTSJMSEMOURCLNOIETIHEUCMPLWKDXDO9TAHVH99MKTBAAKCMYKLJUQIVLLSVTFUM9KDSIHYXYHPRLDADSLSSOIGLLXMPKTHS9YXUNMUTBTBPDWXA9GVTBGLTCLEZEUNNIRBBURDWOFFYXELPFSZRQARVRPHGETKJTRUZIFDDWBOHHGUZTODZFMOVMAGCYCTGBWSGAVZADIPIASCKTRKIUUMHNGUYZKDVOPKKHXD9EXVUVJ9YFNYMLIJLEEGPIZLFS9FIEMG9MIEO9FPW9JZEVDQOECMTESICSMVWXZNXXJILJLVQHEBHQWPOBHKEGRLFCPLB9ZECJOZDAB9DMU9UALBIQDABVDYRRTPMZOCQX9WNGXVNKQZWPA9ACVONQMRHQDPPIQTP9VKP9PAORNOFTZZWGC9RYBWSNLULZGYLMYIWWPDMOHPZTQWRPRCN9RAUOKDSCWBRI9NPUPLBILOZDOOPHSWQGJEGUYWAWJDEBLEOBSYYU9XSRPBHRUQXIDOWJZQQVJTMP9VLWLOGBK9FZFHYLJCNENDATNPSF99DFPVPTNNKIUMHRGEBJXNUVENAHYLFPPHYFTIKCB9DBVCCSJTDMOMISBAAEJVBVLHOADKNFG9NQGIGRDICQCWZVHGGXLTUNQKBUTLDWXIM9REWBLIXFBPTOXBLWBQQUSRLRDHTXQWARPMBQILAJSYLLTDAGTFPCXBCDITDOIZNGKPZQWWHJDZIPYCPFEYFD9CVXYOJHJNUNMCMSIAUVSKCACNNPGDYJJVTZOREJOPIBYCMBULMTSDTJPZNVNYQBQPPABOSSNZJKQQZ9LULSHJUBLHIFMYWSNPGUERCLVFV9LOEBJEERYHI9OMSMSCDFDLNHEMLQXNRJDYSNKTOYCPTAUWAWIGCPJKMAMGLXNBJMO9BZGFIHWDVJWYCNZZV9KBWIFQSMAXBPGVXDW9SLTHOLMJORRXZJSTNOQDRGNBLGTFCCNBJECYZGWTDRJKJRBAJRCULMOUBQJFWCLWMEWGAAVNZWMDWBYDKZMUCZAKXQLRQPIQJPMORKJXKSDTGXWDHAKUOSMXCFXWSZYWXODWFACBMFSWQFVMBELPZMISVWRQQQPNHOTWOEQQAQJDLXFEEBXLJQEECWG9ARRRDLTVBHTPARJMLOZHYWDCSXPTZCNZWTCRUJNZWKFZXAARPHFCBTLWSLERGJJMKIG9NEBADRMZWYNWIRGTMOBRKURUE9GDLRIEODY9BXJOZUVNCXKXFPFDXKUTMXZRJDOQ9YTV9BJDKGZBYTWGVPQQMNVCNARLPSRQWN9TRMHWLNEJZFTCSRD"; - - private static readonly string SIGNATURE2 = - "URKFKLNXFEKDOGSQVMAOPEDIWSMTCKJZ9KEVWYALY9JAO9KHUGNDTMGQLKQJUIPWDIVMPEDSVPLFMDCIXDDT9WBBRTFQENL9AXLSBYHINXCDYBFGRNKJDYHAQVJKWCVOYXHTNBEZUNLVMJLUMZYJFAOW9PVVMJZNZZFJQEQFELVFZVFVWPJ9WQZJLPSGBYECHXSFVFQJGUCPFXC9GATTILVCAANNHOYMLOYX9QSUPCERYCOXPACZEEGLREBRZWXGUTTVTHB9GBRCIFEOBPIRXXPQKRSODEHDSZXLGIKXUQWNTQKIOPVDVSIK9WJUAEFOJBU9MBPBSVYSCLBMINTT9ZCTREZSMSVOPXSZOMCGFEZKMOCNLJ9QUTAPKBHRIAIYLCHUQHOINKSCMXWZVDGDXHNJQXJHPCCGBEWROVKEPAPBFFRCAVXZWIRKCRAWYHIHMDXFAGDJQNJJPYSQUHKFOOCEVQOGRQEIOQFKZWUQ9XVRNXKGMJOQEZHQZXQABWUQRBKXWHYUXEAEMDGXVY9WS9VJOCMGBQASSRNKAYJPTSPQEMYSJMTCLMDQJKDPBGQZZSFBDOKHBYY9UDRXNKTPWBCQTVKUGMEDUXL9TTKPATNIKVAGHACHPFSCRYNIRJBQC9OADPGWBFYYARSVNQCGMYQGCYLZH9KLMUIJPCLPQVS9BORXCJBXPDECJGKDNOUYWTKKFLXZARWKGUSMVMXKJTMRYZRERFCFGTZFZFCAOQSZGPQJUEZUJLJPU9QPMJUTZNLMSMPRGIFHUUZHMPMRBEBATEIIWPCOIMWOYOG9NYFBYOWFDKRXOTREBU99GNCPXKOWGI99LNVPRFFF9FCLFXI9HMUFU9NRLNJVTFNUSUJTAVOG9GKUYYEXIM9HTPIDTWIGLKRAQPKMQVZAPYMPSQIOJ9JZBWDMQHDSSRSHNCWSAJCSRORSEXLLQNZUKPXPGRLYMXOXWCCWWSBALFLXPHSGFLTOAFWPETBKJUMBLHMSKYLPJT9EJAZCPPNZWKPVCGKDJCRCLBBIAKVDSNWGONPLKFAYXZDI9FKPHDPKCB9UUPXLJVQTXOAZOQDRNSONXDVSLQGZYRIPGREYHRAUOSBFZDZPZHFNMWCZQGPXCZVLNCSASB9RQDFHOYMUVYLFKOEEWNREYCDMCTZIAFBFKLKRQWZCJHQZCZGWXIFTKRVMPHMVHAABHBDEV9WDEZBR9FLXLNBVNYKUOUFJQKNZVZVGZDDTFYNYFUVRLZKOLXXQYNV9MDVBLZSERXPGYKRIEZQZD9IBKFDT9AIYGWJJCXFWDUDURGJQLXVEJAVEOMZUVVTNCVBXEVQRDQIEHDUCSLCIJUTSCLFXEGMFYP9YLXELCZPMTBZWBIODZCFNJLVWTPQGLMQIHIABAYGJFFMOEDTCXGEDTNXMVXZYFGXRKVVRTIZ9ISXTDHAFPEKQZSM9XXQLOYBLTMD9MBERBIBEJDEXGMOLDZPZVVEPIRKJBDPAKFAWJPTCJSHZPDUKZEEHRFLMZCUGCOWFJBSTDGPHUIXSPPPHRQARMCFMTWKYPJNJQV9VSFZ9EWB9GVEAFUXHWRNUXQLCSBWROOITBATWUXUYGSMGAXKGEBP9ZJWXQWHBVPOSLDHTWXUOFQNO9EXSYPQF9LQLQAFNRU9MTIIRQLBBBYKUPANWRQKGESFARQIRUTGFMZVUKHZJYKTYOARTDOBIYBFRHJWEFHCYVHRHTLTWBRMUDVIVQVNELQMQRXYDNGVSICZINWIZCIWVFXLYOLYKWDNWCWFZUXHUWOPRDHMTSXOZX9CVHANU9ZXTJOGKEPYR9CHGOTIUQSWIALAOIKHQFXWY9ZWTSZADVXJNNZOLSCXVVFBRHLRBTGMSZOYNIXTAMABKGJTLGTZKRHOPPJMNYIQNVKRGXUQDWYEIEZYM9CSXO9YLSBJLDJUWOLUXDEKBGGEIDEXFLZMESDOITNYTNRLGOMHJH9HOLXJABUNLXCZYTXFPZMHRJPLXSVPDBJBBZX9TBIMZZFZOXUSFEJYHEXPFXGJCQTBBLPEEWAPHUETGXSXYYAF9PCCCOONRMQGAPJ9JO9BZQ9QSKTPFFYIFVHSLAZY9CWYSIMKDOSLRKWBHPGJGVEJEEMLCCWXKSOCMBMZZZJWYBBXE9FTAYJALGWITJRXAXWZEXMECTZEEIWZPHYX"; - - private static readonly string ADDR_SEED = - "LIESNFZLPFNWAPWXBLKEABZEEWUDCXKTRKZIRTPCKLKWOMJSEREWKMMMODUOFWM9ELEVXADTSQWMSNFVD"; - - private static readonly string ADDR_I0_S1 = - "HIPPOUPZFMHJUQBLBVWORCNJWAOSFLHDWF9IOFEYVHPTTAAF9NIBMRKBICAPHYCDKMEEOXOYHJBMONJ9D"; - - private static readonly string ADDR_I0_S2 = - "BPYZABTUMEIOARZTMCDNUDAPUOFCGKNGJWUGUXUKNNBVKQARCZIXFVBZAAMDAFRS9YOIXWOTEUNSXVOG9"; - - private static readonly string ADDR_I0_S3 = - "BYWHJJYSHSEGVZKKYTJTYILLEYBSIDLSPXDLDZSWQ9XTTRLOSCBCQ9TKXJYQAVASYCMUCWXZHJYRGDOBW"; - - private static readonly string ADDR_LS_I0_S1 = - "VKPCVHWKSCYQNHULMPYDZTNKOQHZNPEGJVPEHPTDIUYUBFKFICDRLLSIULHCVHOHZRHJOHNASOFRWFWZC"; - - private static readonly string ADDR_LS_I0_S2 = - "PTHVACKMXOKIERJOFSRPBWCNKVEXQ9CWUTIJGEUORSKWEDDJCBFQCCBQZLTYXQCXEDWLTMRQM9OQPUGNC"; - - private static readonly string ADDR_LS_I0_S3 = - "AGSAAETPMSBCDOSNXFXIOBAE9MVEJCSWVP9PAULQ9VABOTWLDMXID9MXCCWQIWRTJBASWPIJDFUC9ISWD"; - - [TestMethod] - public void TestAddressGeneration() - { - Assert.AreEqual(FIRST_ADDR, IotaApiUtils.NewAddress(TEST_SEED, 2, 0, true, null)); - Assert.AreEqual(SIXTH_ADDR, IotaApiUtils.NewAddress(TEST_SEED, 2, 5, true, null)); - - Assert.AreEqual(ADDR_I0_S1, IotaApiUtils.NewAddress(ADDR_SEED, 1, 0, false, null)); - Assert.AreEqual(ADDR_I0_S2, IotaApiUtils.NewAddress(ADDR_SEED, 2, 0, false, null)); - Assert.AreEqual(ADDR_I0_S3, IotaApiUtils.NewAddress(ADDR_SEED, 3, 0, false, null)); - - Assert.AreEqual(ADDR_LS_I0_S1, IotaApiUtils.NewAddress(ADDR_SEED + ADDR_SEED, 1, 0, false, null)); - Assert.AreEqual(ADDR_LS_I0_S2, IotaApiUtils.NewAddress(ADDR_SEED + ADDR_SEED, 2, 0, false, null)); - Assert.AreEqual(ADDR_LS_I0_S3, IotaApiUtils.NewAddress(ADDR_SEED + ADDR_SEED, 3, 0, false, null)); - } - - [TestMethod] - public void TestLongSeedKeyGeneration() - { - ICurl curl = new Kerl(); - var signing = new Signing(curl); - var seed = "EV9QRJFJZVFNLYUFXWKXMCRRPNAZYQVEYB9VEPUHQNXJCWKZFVUCTQJFCUAMXAHMMIUQUJDG9UGGQBPIY"; - - for (var i = 1; i < 5; i++) - { - var key1 = signing.Key(Converter.ToTrits(seed), 0, i); - Assert.AreEqual(Signing.KeyLength * i, key1.Length); - var key2 = signing.Key(Converter.ToTrits(seed + seed), 0, i); - Assert.AreEqual(Signing.KeyLength * i, key2.Length); - var key3 = signing.Key(Converter.ToTrits(seed + seed + seed), 0, i); - Assert.AreEqual(Signing.KeyLength * i, key3.Length); - } - } - - [TestMethod] - public void TestSigning() - { - // we can sign any hash, so for convenience we will sign the first - // address of our test seed - // (but remove the checksum) with the key of our fifth address - var hashToSign = FIRST_ADDR.RemoveChecksum(); - var signing = new Signing(null); - var key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 2); - var normalizedHash = new Bundle().NormalizedBundle(hashToSign); - - var subKey = new int[6561]; - var subNormalizedHash = new int[27]; - - Array.Copy(key, 0, subKey, 0, 6561); - Array.Copy(normalizedHash, 0, subNormalizedHash, 0, 27); - var signature = signing.SignatureFragment( - subNormalizedHash, - subKey); - Assert.AreEqual(SIGNATURE1, Converter.ToTrytes(signature)); - - Array.Copy(key, 6561, subKey, 0, 6561); - Array.Copy(normalizedHash, 27, subNormalizedHash, 0, 27); - var signature2 = signing.SignatureFragment( - subNormalizedHash, - subKey); - Assert.AreEqual(SIGNATURE2, Converter.ToTrytes(signature2)); - } - - [TestMethod] - public void TestKeyLength() - { - var signing = new Signing(null); - var key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 1); - Assert.AreEqual(Signing.KeyLength, key.Length); - key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 2); - Assert.AreEqual(2 * Signing.KeyLength, key.Length); - key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 3); - Assert.AreEqual(3 * Signing.KeyLength, key.Length); - } - - [TestMethod] - public void TestVerifying() - { - var signing = new Signing(null); - Assert.IsTrue(signing.ValidateSignatures( - RemoveChecksum(SIXTH_ADDR), - new[] {SIGNATURE1, SIGNATURE2}, - RemoveChecksum(FIRST_ADDR))); - } - - private string RemoveChecksum(string address) - { - Assert.IsTrue(address.IsValidChecksum()); - return address.Substring(0, 81); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/Utils/TrytesConverterTest.cs b/IotaCSharpApiUnitTests/Api/Utils/TrytesConverterTest.cs deleted file mode 100644 index f53c195..0000000 --- a/IotaCSharpApiUnitTests/Api/Utils/TrytesConverterTest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using Iota.Lib.CSharp.Api.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Iota.Lib.CSharpTests.Api.Utils -{ - [TestClass] - public class TrytesConverterTest - { - private static readonly Random Random = new Random(); - - [TestMethod] - public void ShouldConvertStringToTrytes() - { - Assert.AreEqual("IC", TrytesConverter.ToTrytes("Z")); - Assert.AreEqual(TrytesConverter.ToTrytes("JOTA JOTA"), "TBYBCCKBEATBYBCCKB"); - } - - [TestMethod] - public void ShouldConvertTrytesToString() - { - Assert.AreEqual("Z", TrytesConverter.ToString("IC")); - Assert.AreEqual(TrytesConverter.ToString("TBYBCCKBEATBYBCCKB"), "JOTA JOTA"); - } - - [TestMethod] - public void ShouldConvertBackAndForth() - { - var str = RandomString(1000); - var back = TrytesConverter.ToString(TrytesConverter.ToTrytes(str)); - Assert.AreEqual(str, back); - } - - public static string RandomString(int length) - { - const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[Random.Next(s.Length)]).ToArray()); - } - } -} \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/IotaCSharpApiUnitTests.csproj b/IotaCSharpApiUnitTests/IotaCSharpApiUnitTests.csproj deleted file mode 100644 index e920b04..0000000 --- a/IotaCSharpApiUnitTests/IotaCSharpApiUnitTests.csproj +++ /dev/null @@ -1,109 +0,0 @@ - - - - Debug - AnyCPU - {FAE71C0D-C373-4401-B9DE-BC3DC1D4E435} - Library - Properties - Iota.Lib.CSharpTests - IotaCSharpApiUnitTests - v4.6.1 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {fc2c2f96-49ea-4046-95bd-3b570bdd1e13} - IotaCSharpApi - - - - - - - - - - False - - - False - - - False - - - False - - - - - - - - \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Properties/AssemblyInfo.cs b/IotaCSharpApiUnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index fe68fc0..0000000 --- a/IotaCSharpApiUnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("IotaCSharpApiUnitTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("IotaCSharpApiUnitTests")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("fae71c0d-c373-4401-b9de-bc3dc1d4e435")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/packages.config b/IotaCSharpApiUnitTests/packages.config deleted file mode 100644 index 2df57d0..0000000 --- a/IotaCSharpApiUnitTests/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Src/IotaSharp.MAM.Tests/Core/MamTest.cs b/Src/IotaSharp.MAM.Tests/Core/MamTest.cs new file mode 100644 index 0000000..2b1ddf8 --- /dev/null +++ b/Src/IotaSharp.MAM.Tests/Core/MamTest.cs @@ -0,0 +1,148 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using IotaSharp.MAM.Core; +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.MAM.Tests.Core +{ + [TestClass] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class MamTest + { + private const string Provider = "http://node05.iotatoken.nl:16265"; + + [TestMethod] + public void PublishPublicTest() + { + var iota = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + var mam = new Mam(iota); + string seed = "CNHIRWBWVPDBGHKYZDJEZVIRDBSEDTCRBESFXOGRSWWDQVRNQATQUKIVDUDINJKKNCULQFCWWIG9LAEHQ"; + + // ReSharper disable once RedundantArgumentDefaultValue + var mamState = mam.InitMamState(seed, 2); + + // Create MAM Payload + var mamMessage = mam.CreateMamMessage(mamState, TrytesConverter.AsciiToTrytes("POTATO")); + + Console.WriteLine($"Root: {mamMessage.Root}"); + Console.WriteLine($"Address: {mamMessage.Address}"); + + // Attach the payload + mam.Attach(mamMessage.Payload, mamMessage.Address); + + // Fetch Stream Async to Test + var result = mam.Fetch(mamMessage.Root, MamMode.Public); + + Console.WriteLine("Fetch result:"); + foreach (var message in result.Item1) + { + Console.WriteLine(message); + } + + Console.WriteLine($"NextRoot:{result.Item2}"); + + } + + [TestMethod] + public void PublishPrivateTest() + { + var iota = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + var mam = new Mam(iota); + + // Initialise MAM State + var mamState = mam.InitMamState(); + mamState.ChangeMode(MamMode.Private); + + // Create MAM Payload + var mamMessage = mam.CreateMamMessage(mamState, "POTATO"); + + Console.WriteLine($"Root: {mamMessage.Root}"); + Console.WriteLine($"Address: {mamMessage.Address}"); + + // Attach the payload + mam.Attach(mamMessage.Payload, mamMessage.Address); + + // Fetch Stream Async to Test + var result = mam.Fetch(mamMessage.Root, MamMode.Private); + + Console.WriteLine("Fetch result:"); + foreach (var message in result.Item1) + { + Console.WriteLine(message); + } + + Console.WriteLine($"NextRoot:{result.Item2}"); + } + + [TestMethod] + public void PublishRestrictedTest() + { + var iota = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + var mam = new Mam(iota); + string sideKey = "IREALLYENJOYPOTATORELATEDPRODUCTS"; + + // Initialise MAM State + var mamState = mam.InitMamState(); + mamState.ChangeMode(MamMode.Restricted, sideKey); + + // Create MAM Payload + var mamMessage = mam.CreateMamMessage(mamState, "POTATO"); + + Console.WriteLine($"Root: {mamMessage.Root}"); + Console.WriteLine($"Address: {mamMessage.Address}"); + + // Attach the payload + mam.Attach(mamMessage.Payload, mamMessage.Address); + + // Fetch Stream Async to Test + var result = mam.Fetch(mamMessage.Root, MamMode.Restricted, sideKey); + + Console.WriteLine("Fetch result:"); + foreach (var message in result.Item1) + { + Console.WriteLine(message); + } + + Console.WriteLine($"NextRoot:{result.Item2}"); + } + + [TestMethod] + public void SingleLeafTree() + { + var iota = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + var mam = new Mam(iota); + string seed = "TX9XRR9SRCOBMTYDTMKNEIJCSZIMEUPWCNLC9DPDZKKAEMEFVSTEVUFTRUZXEHLULEIYJIEOWIC9STAHW"; + string message = "POTATO"; + + var mamState = mam.InitMamState(seed, 1); + mamState.Channel.Security = 1; + mamState.Channel.Start = 0; + mamState.Channel.Count = 1; + mamState.Channel.NextCount = 1; + + var mamMessage = mam.CreateMamMessage(mamState, message); + + // parse + var unmaskedMessage = + mam.DecodeMessage(mamMessage.Payload, + mamMessage.State.Channel.SideKey, + mamMessage.Root); + + // compare + Assert.AreEqual(message, unmaskedMessage.Item1); + } + } +} diff --git a/Src/IotaSharp.MAM.Tests/Core/MaskTest.cs b/Src/IotaSharp.MAM.Tests/Core/MaskTest.cs new file mode 100644 index 0000000..a3d52db --- /dev/null +++ b/Src/IotaSharp.MAM.Tests/Core/MaskTest.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using IotaSharp.MAM.Core; +using IotaSharp.Pow; +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.MAM.Tests.Core +{ + [TestClass] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class MaskTest + { + [TestMethod] + public void ItCanUnmask() + { + sbyte[] payload = Converter.ToTrits( + "AAMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9MESSAGEFORYOU9"); + sbyte[] keys = Converter.ToTrits("MYMERKLEROOTHASH"); + + //int index = 5; + ICurl curl = new Curl(SpongeFactory.Mode.CURLP27); + //add_assign(&mut keys, index as isize); + sbyte[] cipher = new sbyte[payload.Length]; + Array.Copy(payload, cipher, payload.Length); + + MaskHelper.Mask(cipher, keys, curl); + curl.Reset(); + MaskHelper.UnMask(cipher, keys, curl); + + Assert.AreEqual(Converter.ToTrytes(cipher), Converter.ToTrytes(payload)); + + } + + [TestMethod] + public void ItCanUnmaskSlice() + { + sbyte[] payload = Converter.ToTrits( + "AAMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9AMESSAGEFORYOU9MESSAGEFORYOU9"); + ICurl curl = new Curl(SpongeFactory.Mode.CURLP27); + + sbyte[] cipher = new sbyte[payload.Length]; + Array.Copy(payload, cipher, payload.Length); + + MaskHelper.MaskSlice(cipher, curl); + curl.Reset(); + MaskHelper.UnMaskSlice(cipher, curl); + + Assert.AreEqual(Converter.ToTrytes(cipher), Converter.ToTrytes(payload)); + } + } +} diff --git a/Src/IotaSharp.MAM.Tests/IotaSharp.MAM.Tests.csproj b/Src/IotaSharp.MAM.Tests/IotaSharp.MAM.Tests.csproj new file mode 100644 index 0000000..6f8213c --- /dev/null +++ b/Src/IotaSharp.MAM.Tests/IotaSharp.MAM.Tests.csproj @@ -0,0 +1,31 @@ + + + + net461;netcoreapp2.2 + $(LibraryFrameworks) + latest + IotaSharp.MAM.Tests + IotaSharp.MAM.Tests + false + False + Full + false + + + + + + + + + + + + + + + + + + + diff --git a/Src/IotaSharp.MAM.Tests/Merkle/MerkleTreeTest.cs b/Src/IotaSharp.MAM.Tests/Merkle/MerkleTreeTest.cs new file mode 100644 index 0000000..4763e64 --- /dev/null +++ b/Src/IotaSharp.MAM.Tests/Merkle/MerkleTreeTest.cs @@ -0,0 +1,45 @@ +using IotaSharp.MAM.Merkle; +using IotaSharp.Pow; +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.MAM.Tests.Merkle +{ + [TestClass] + public class MerkleTreeTest + { + [TestMethod] + public void ItDoesNotPanic() + { + // ReSharper disable StringLiteralTypo + string seed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ9ABCDEFGHIJKLMNOPQRSTUVWXYZ9ABCDEFGHIJKLMNOPQRSTUVWXYZ9"; + // ReSharper restore StringLiteralTypo + seed = InputValidator.PadSeedIfNecessary(seed); + + sbyte[] trits = Converter.ToTrits(seed); + + ICurl c1 = new Curl(SpongeFactory.Mode.CURLP27); + ICurl c2 = new Curl(SpongeFactory.Mode.CURLP27); + ICurl c3 = new Curl(SpongeFactory.Mode.CURLP27); + + int start = 1; + int index = 5; + int treeDepth = 5; + int leafCount = 9; + int security = 1; + //int[] digest = new int[MAM.Utils.Constants.DigestLength]; + + var rootNode = MerkleTree.CreateMerkleTree( + trits, start, (uint) leafCount, security, c1, c2, c3); + + var someBranch = MerkleTree.CreateMerkleBranch(rootNode, index); + + Assert.AreEqual(20, rootNode.Size); + Assert.AreEqual(treeDepth, rootNode.Depth); + Assert.AreEqual(leafCount, rootNode.Count); + + int branchLength = someBranch.Length; + Assert.AreEqual(treeDepth - 1, branchLength); + } + } +} diff --git a/Src/IotaSharp.MAM.Tests/Utils/PascalTest.cs b/Src/IotaSharp.MAM.Tests/Utils/PascalTest.cs new file mode 100644 index 0000000..7893ea3 --- /dev/null +++ b/Src/IotaSharp.MAM.Tests/Utils/PascalTest.cs @@ -0,0 +1,51 @@ +using IotaSharp.MAM.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.MAM.Tests.Utils +{ + [TestClass] + public class PascalTest + { + [TestMethod] + public void EncodeNumbers() + { + for (long i = 0; i < 1000; i++) + { + TestEncoding(i); + } + + for (long i = 0; i < 1000; i++) + { + TestEncoding(-i); + } + + for (long i = 10000000; i < 10000100; i++) + { + TestEncoding(i); + } + + for (long i = 10000000; i < 10000100; i++) + { + TestEncoding(-i); + } + + TestEncoding(long.MaxValue / 1000); + TestEncoding(long.MinValue / 1000); + + } + + private void TestEncoding(long input) + { + int length = Pascal.EncodedLength(input); + sbyte[] trits = new sbyte[length]; + + Pascal.Encode(input, trits); + + var result = Pascal.Decode(trits); + + Assert.AreEqual(input, result.Item1); + Assert.AreEqual(trits.Length, result.Item2); + + } + } +} diff --git a/Src/IotaSharp.MAM/Core/Mam.cs b/Src/IotaSharp.MAM/Core/Mam.cs new file mode 100644 index 0000000..7f84562 --- /dev/null +++ b/Src/IotaSharp.MAM/Core/Mam.cs @@ -0,0 +1,435 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using IotaSharp.MAM.Merkle; +using IotaSharp.MAM.Utils; +using IotaSharp.Model; +using IotaSharp.Pow; +using IotaSharp.Utils; +using Constants = IotaSharp.MAM.Utils.Constants; + +namespace IotaSharp.MAM.Core +{ + /// + /// + /// + public class Mam + { + /// + /// + /// + /// + public Mam(IotaAPI iota) + { + Iota = iota; + } + + /// + /// + /// + public IotaAPI Iota { get; set; } + + /// + /// + /// + /// + /// + /// + public MamState InitMamState(string seed = null, int security = 2) + { + var channel = new MamChannel + { + SideKey = null, + Mode = MamMode.Public, + NextRoot = null, + Security = security, + Start = 0, + Count = 1, + NextCount = 1, + Index = 0 + }; + + if (string.IsNullOrEmpty(seed)) + seed = SeedRandomGenerator.GenerateNewSeed(); + + return new MamState + { + Seed = seed, + Channel = channel + //Subscribed + }; + } + + /// + /// + /// + /// + /// + /// + public MamMessage CreateMamMessage(MamState state, string message) + { + var channel = state.Channel; + var seed = state.Seed; + var sideKey = channel.SideKey; + + if (string.IsNullOrEmpty(sideKey)) + sideKey = "999999999999999999999999999999999999999999999999999999999999999999999999999999999"; + + var seedTrits = Converter.ToTrits(seed); + var messageTrits = Converter.ToTrits(message); + var sideKeyTrits = Converter.ToTrits(sideKey); + + var security = channel.Security; + var start = channel.Start; + var count = channel.Count; + var nextStart = start + count; + var nextCount = channel.NextCount; + var index = channel.Index; + + // set up merkle tree + var rootMerkle = MerkleTree.CreateMerkleTree( + seedTrits, start, (uint) count, security, + new Curl(SpongeFactory.Mode.CURLP27), + new Curl(SpongeFactory.Mode.CURLP27), + new Curl(SpongeFactory.Mode.CURLP27)); + + var nextRootMerkle = MerkleTree.CreateMerkleTree( + seedTrits, nextStart, (uint) nextCount, security, + new Curl(SpongeFactory.Mode.CURLP27), + new Curl(SpongeFactory.Mode.CURLP27), + new Curl(SpongeFactory.Mode.CURLP27)); + + var rootBranch = MerkleTree.CreateMerkleBranch(rootMerkle, index); + sbyte[] rootSiblings = { }; + if (rootBranch != null) + rootSiblings = rootBranch.Siblings; + + var rootTrits = rootMerkle.Slice(); + var nextRootTrits = nextRootMerkle.Slice(); + + var maskedPayload = + CreateMaskedPayload(seedTrits, messageTrits, + sideKeyTrits, rootTrits, rootSiblings, nextRootTrits, + start, index, security); + + var root = Converter.ToTrytes(rootTrits); + var nextRoot = Converter.ToTrytes(nextRootTrits); + var payload = Converter.ToTrytes(maskedPayload); + + // If the tree is exhausted. + if (channel.Index == channel.Count - 1) + { + // change start to begining of next tree. + channel.Start = channel.NextCount + channel.Start; + // Reset index. + channel.Index = 0; + } + else + { + //Else step the tree. + channel.Index++; + } + + // Advance Channel + channel.NextRoot = nextRoot; + state.Channel = channel; + + // Generate attachement address + var address = channel.Mode != MamMode.Public ? HashHelper.Hash(root) : root; + + return new MamMessage + { + State = state, + Payload = payload, + Root = root, + Address = address + }; + } + + private sbyte[] CreateMaskedPayload( + sbyte[] seedTrits, sbyte[] messageTrits, + sbyte[] keyTrits, sbyte[] rootTrits, + sbyte[] siblingsTrits, sbyte[] nextRootTrits, + int start, int index, int security) + { + ICurl curl = new Curl(SpongeFactory.Mode.CURLP27); + ICurl encrCurl = new Curl(SpongeFactory.Mode.CURLP27); + var hammingNonce = new HammingNonce(SpongeFactory.Mode.CURLP27); + + var minLength = GetPayloadMinLength(messageTrits.Length, siblingsTrits.Length, index, security); + var payloadLength = (int) TritsHelper.RoundThird(minLength); + var payload = new sbyte[payloadLength]; + + // Creates a signed, encrypted payload from a message + + // generate the key and the get the merkle tree hashes + var messageLength = messageTrits.Length; + + var indexP = Pascal.EncodedLength(index); + var messageP = Pascal.EncodedLength(messageLength); + + var siblingsLength = siblingsTrits.Length; + var siblingsCount = siblingsTrits.Length / Constants.HashLength; + var siblingsPascalLength = Pascal.EncodedLength(siblingsCount); + var signatureLength = security * Constants.KeyLength; + + var nextRootStart = indexP + messageP; + var nextEnd = nextRootStart + nextRootTrits.Length; + var messageEnd = nextRootStart + Constants.HashLength + messageLength; + var nonceEnd = messageEnd + Constants.MessageNonceLength; + var signatureEnd = nonceEnd + signatureLength; + var siblingsPascalEnd = signatureEnd + siblingsPascalLength; + var siblingsEnd = siblingsPascalEnd + siblingsLength; + + encrCurl.Absorb(keyTrits); + encrCurl.Absorb(rootTrits); + + var trits = new sbyte[indexP]; + Pascal.Encode(index, trits); + Array.Copy(trits, 0, payload, 0, indexP); + + trits = new sbyte[messageP]; + Pascal.Encode(messageLength, trits); + Array.Copy(trits, 0, payload, indexP, messageP); + + encrCurl.Absorb(payload, 0, nextRootStart); + Array.Copy(nextRootTrits, 0, payload, nextRootStart, nextRootTrits.Length); + Array.Copy(messageTrits, 0, payload, nextEnd, messageTrits.Length); + MaskHelper.MaskSlice(payload, nextRootStart, messageEnd - nextRootStart, encrCurl); + + Array.Copy(encrCurl.State, curl.State, encrCurl.State.Length); + + + hammingNonce.Search(security, 0, Constants.HashLength / 3, curl); + Array.Copy(curl.State, 0, payload, messageEnd, Constants.MessageNonceLength); + MaskHelper.MaskSlice(payload, messageEnd, nonceEnd - messageEnd, encrCurl); + + curl.Reset(); + var subseed = HashHelper.Subseed(seedTrits, start + index, curl); + Array.Copy(subseed, 0, payload, nonceEnd, subseed.Length); + + curl.Reset(); + HashHelper.Key(payload, nonceEnd, signatureEnd - nonceEnd, security, curl); + + curl.Reset(); + HashHelper.Signature(encrCurl.Rate, payload, nonceEnd, signatureEnd - nonceEnd, curl); + + curl.Reset(); + + trits = new sbyte[siblingsPascalLength]; + Pascal.Encode(siblingsCount, trits); + Array.Copy(trits, 0, payload, signatureEnd, siblingsPascalLength); + Array.Copy(siblingsTrits, 0, payload, siblingsPascalEnd, siblingsLength); + + MaskHelper.MaskSlice(payload, nonceEnd, siblingsEnd - nonceEnd, encrCurl); + encrCurl.Reset(); + + return payload; + } + + private int GetPayloadMinLength(int messageTritsLength, int siblingsTritsLength, int index, int security) + { + return + Pascal.EncodedLength(index) + + Pascal.EncodedLength(Constants.HashLength + messageTritsLength) + Constants.HashLength + + messageTritsLength + Constants.MessageNonceLength + security * Constants.KeyLength + + Pascal.EncodedLength(siblingsTritsLength / Constants.HashLength) + siblingsTritsLength; + } + + public void Attach(string trytes, string address, int depth = 6, int mwm = 14) + { + var tag = "999999999999999999999999999"; + var transfers = new List + { + new Transfer(address.AddChecksum(), 0, trytes, tag) + }; + + // ReSharper disable once UnusedVariable + var result = Iota.SendTransfer( + SeedRandomGenerator.GenerateNewSeed(), 2, depth, mwm, transfers.ToArray(), + null, null, + false, true, null); + } + + public Tuple, string> Fetch(string root, MamMode mode, string sideKey = null, + Action action = null) + { + var messages = new List(); + + var nextRoot = root; + var lastNextRoot = root; + + // ReSharper disable NotAccessedVariable + var transactionCount = 0; + var messageCount = 0; + // ReSharper restore NotAccessedVariable + + while (true) + { + // Apply channel mode + var address = mode != MamMode.Public ? HashHelper.Hash(nextRoot) : nextRoot; + + var response = Iota.IotaClient.FindTransactionsByAddresses(address); + + if (response.Hashes.Count == 0) + break; + + transactionCount += response.Hashes.Count; + messageCount++; + + var messagesGen = TransactionsToMessages(response.Hashes); + foreach (var payload in messagesGen) + try + { + var unmaskedMessage = DecodeMessage(payload, sideKey, nextRoot); + var message = unmaskedMessage.Item1; + lastNextRoot = unmaskedMessage.Item2; + + if (action != null) + action.Invoke(message); + else + messages.Add(message); + } + catch (System.Exception e) + { + Console.WriteLine(e); + } + + nextRoot = lastNextRoot; + } + + return new Tuple, string>(messages, nextRoot); + } + + /// + /// + /// + /// + /// + /// (message,nextRoot) + public Tuple DecodeMessage(string payload, string sideKey, string root) + { + if (string.IsNullOrEmpty(sideKey)) + sideKey = "999999999999999999999999999999999999999999999999999999999999999999999999999999999"; + + var payloadTrits = Converter.ToTrits(payload); + var sideKeyTrits = Converter.ToTrits(sideKey); + var rootTrits = Converter.ToTrits(root); + + ICurl curl = new Curl(SpongeFactory.Mode.CURLP27); + + // parse + var result = Pascal.Decode(payloadTrits); + var index = (int) result.Item1; + var indexEnd = result.Item2; + + var tempTrits = new sbyte[payloadTrits.Length - indexEnd]; + Array.Copy(payloadTrits, indexEnd, tempTrits, 0, tempTrits.Length); + + result = Pascal.Decode(tempTrits); + var messageLength = (int) result.Item1; + var messageLengthEnd = result.Item2; + + var nextRootStart = indexEnd + messageLengthEnd; + var messageStart = nextRootStart + Constants.HashLength; + var messageEnd = messageStart + messageLength; + + curl.Absorb(sideKeyTrits); + curl.Absorb(rootTrits); + + if (messageLength > payloadTrits.Length) + throw new ArgumentOutOfRangeException(); + + curl.Absorb(payloadTrits, 0, nextRootStart); + + MaskHelper.UnMaskSlice(payloadTrits, nextRootStart, messageStart - nextRootStart, curl); + MaskHelper.UnMaskSlice(payloadTrits, messageStart, messageEnd - messageStart, curl); + + var pos = messageEnd; + MaskHelper.UnMaskSlice(payloadTrits, pos, Constants.MessageNonceLength, curl); + pos += Constants.HashLength / 3; + + var hmac = new sbyte[Constants.HashLength]; + Array.Copy(curl.Rate, hmac, Constants.HashLength); + + var security = HashHelper.CheckSumSecurity(hmac); + MaskHelper.UnMaskSlice(payloadTrits, pos, payloadTrits.Length - pos, curl); + + if (security == 0) + { + // InvalidHash + curl.Reset(); + throw new ApplicationException(); + } + + + var sigEnd = pos + security * Constants.KeyLength; + HashHelper.DigestBundleSignature(hmac, payloadTrits, pos, sigEnd - pos, curl); + + Array.Copy(curl.Rate, hmac, Constants.HashLength); + curl.Reset(); + + pos = sigEnd; + tempTrits = new sbyte[payloadTrits.Length - pos]; + Array.Copy(payloadTrits, pos, tempTrits, 0, tempTrits.Length); + result = Pascal.Decode(tempTrits); + + curl.Absorb(hmac); + if (result.Item1 != 0) + { + // get address lite + Array.Copy(curl.Rate, hmac, Constants.HashLength); + pos += result.Item2; + var sibEnd = pos + (int) result.Item1; + + var siblings = new sbyte[sibEnd - pos]; + Array.Copy(payloadTrits, pos, siblings, 0, siblings.Length); + + curl.Reset(); + MerkleTree.Root(hmac, siblings, index, curl); + } + + if (!curl.Rate.SequenceEqual(rootTrits)) + { + // InvalidSignature + curl.Reset(); + throw new ApplicationException(); + } + + var message = Converter.ToTrytes(payloadTrits, messageStart, messageLength); + var nextRoot = Converter.ToTrytes(payloadTrits, nextRootStart, Constants.HashLength); + return new Tuple(message, nextRoot); + } + + private List TransactionsToMessages(List hashes) + { + var messages = new List(); + + var transactions = Iota.FindTransactionObjectsByHashes(hashes.ToArray()); + + var bundles + = new Dictionary>(); + + foreach (var transaction in transactions) + { + if (bundles.ContainsKey(transaction.Bundle)) + bundles[transaction.Bundle].Add(transaction); + else + bundles.Add(transaction.Bundle, new List {transaction}); + + if (bundles[transaction.Bundle].Count == transaction.LastIndex + 1) + { + bundles[transaction.Bundle].Sort((x, y) => x.CurrentIndex.CompareTo(y.CurrentIndex)); + + var message = bundles[transaction.Bundle] + .Aggregate("", (acc, e) => acc + e.SignatureMessageFragment); + + if (!string.IsNullOrEmpty(message)) + messages.Add(message); + } + } + + return messages; + } + } +} \ No newline at end of file diff --git a/Src/IotaSharp.MAM/Core/MamChannel.cs b/Src/IotaSharp.MAM/Core/MamChannel.cs new file mode 100644 index 0000000..5ec08a4 --- /dev/null +++ b/Src/IotaSharp.MAM/Core/MamChannel.cs @@ -0,0 +1,17 @@ +namespace IotaSharp.MAM.Core +{ + /// + /// + /// + public class MamChannel + { + public string SideKey { get; set; } + public MamMode Mode { get; set; } + public string NextRoot { get; set; } + public int Security { get; set; } + public int Start { get; set; } + public int Count { get; set; } + public int NextCount { get; set; } + public int Index { get; set; } + } +} \ No newline at end of file diff --git a/Src/IotaSharp.MAM/Core/MamMessage.cs b/Src/IotaSharp.MAM/Core/MamMessage.cs new file mode 100644 index 0000000..2f64013 --- /dev/null +++ b/Src/IotaSharp.MAM/Core/MamMessage.cs @@ -0,0 +1,10 @@ +namespace IotaSharp.MAM.Core +{ + public class MamMessage + { + public MamState State { get; set; } + public string Payload { get; set; } + public string Root { get; set; } + public string Address { get; set; } + } +} \ No newline at end of file diff --git a/Src/IotaSharp.MAM/Core/MamMode.cs b/Src/IotaSharp.MAM/Core/MamMode.cs new file mode 100644 index 0000000..7162eb0 --- /dev/null +++ b/Src/IotaSharp.MAM/Core/MamMode.cs @@ -0,0 +1,9 @@ +namespace IotaSharp.MAM.Core +{ + public enum MamMode + { + Public, + Private, + Restricted + } +} \ No newline at end of file diff --git a/Src/IotaSharp.MAM/Core/MamState.cs b/Src/IotaSharp.MAM/Core/MamState.cs new file mode 100644 index 0000000..a00e142 --- /dev/null +++ b/Src/IotaSharp.MAM/Core/MamState.cs @@ -0,0 +1,17 @@ +namespace IotaSharp.MAM.Core +{ + public class MamState + { + public string Seed { get; set; } + public MamChannel Channel { get; set; } + + public void ChangeMode(MamMode mode, string sideKey = null) + { + Channel.Mode = mode; + if (mode == MamMode.Restricted) + { + Channel.SideKey = sideKey; + } + } + } +} \ No newline at end of file diff --git a/Src/IotaSharp.MAM/Core/MaskHelper.cs b/Src/IotaSharp.MAM/Core/MaskHelper.cs new file mode 100644 index 0000000..bb8cd9c --- /dev/null +++ b/Src/IotaSharp.MAM/Core/MaskHelper.cs @@ -0,0 +1,173 @@ +using System; +using IotaSharp.MAM.Utils; +using IotaSharp.Pow; + +namespace IotaSharp.MAM.Core +{ + /// + /// + /// + public class MaskHelper + { + /// + /// + /// + /// + /// + /// + public static void Mask(sbyte[] payload, sbyte[] key, ICurl curl) + { + Mask(payload, 0, payload.Length, key, 0, key.Length, curl); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void Mask( + sbyte[] payload, int payloadIndex, int payloadLength, + sbyte[] key, int keyIndex, int keyLength, + ICurl curl) + { + curl.Absorb(key, keyIndex, keyLength); + var keyChunk = new sbyte[Constants.HashLength]; + curl.Squeeze(keyChunk); + + for (var i = 0; i < payloadLength; i += Constants.HashLength) + { + var left = payloadLength - i; + var length = left > Constants.HashLength ? Constants.HashLength : left; + + for (var n = 0; n < length; n++) + keyChunk[n] = TritsHelper.Sum(payload[payloadIndex + i + n], keyChunk[n]); + + curl.Absorb(payload, payloadIndex + i, length); + Array.Copy(keyChunk, 0, payload, payloadIndex + i, length); + Array.Copy(curl.Rate, keyChunk, length); + } + } + + /// + /// + /// + /// + /// + /// + public static void UnMask(sbyte[] payload, sbyte[] key, ICurl curl) + { + UnMask(payload, 0, payload.Length, key, 0, key.Length, curl); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void UnMask( + sbyte[] payload, int payloadIndex, int payloadLength, + sbyte[] key, int keyIndex, int keyLength, + ICurl curl) + { + curl.Absorb(key, keyIndex, keyLength); + var keyChunk = new sbyte[Constants.HashLength]; + curl.Squeeze(keyChunk); + + for (var i = 0; i < payloadLength; i += Constants.HashLength) + { + var left = payloadLength - i; + var length = left > Constants.HashLength ? Constants.HashLength : left; + + for (var n = 0; n < length; n++) + keyChunk[n] = TritsHelper.Sum(payload[payloadIndex + i + n], (sbyte) -keyChunk[n]); + + Array.Copy(keyChunk, 0, payload, payloadIndex + i, length); + curl.Absorb(keyChunk, 0, length); + + Array.Copy(curl.Rate, keyChunk, length); + } + } + + /// + /// + /// + /// + /// + public static void MaskSlice(sbyte[] payload, ICurl curl) + { + MaskSlice(payload, 0, payload.Length, curl); + } + + /// + /// + /// + /// + /// + /// + /// + public static void MaskSlice(sbyte[] payload, int payloadIndex, int payloadLength, ICurl curl) + { + var keyChunk = new sbyte[Constants.HashLength]; + Array.Copy(curl.Rate, keyChunk, Constants.HashLength); + + for (var i = 0; i < payloadLength; i += Constants.HashLength) + { + var left = payloadLength - i; + var length = left > Constants.HashLength ? Constants.HashLength : left; + + curl.Absorb(payload, payloadIndex + i, length); + + for (var n = 0; n < length; n++) + payload[payloadIndex + i + n] = TritsHelper.Sum(payload[payloadIndex + i + n], keyChunk[n]); + + Array.Copy(curl.Rate, keyChunk, length); + } + } + + /// + /// + /// + /// + /// + public static void UnMaskSlice(sbyte[] payload, ICurl curl) + { + UnMaskSlice(payload, 0, payload.Length, curl); + } + + /// + /// + /// + /// + /// + /// + /// + public static void UnMaskSlice(sbyte[] payload, int payloadIndex, int payloadLength, ICurl curl) + { + var keyChunk = new int[Constants.HashLength]; + + for (var i = 0; i < payloadLength; i += Constants.HashLength) + { + var left = payloadLength - i; + var length = left > Constants.HashLength ? Constants.HashLength : left; + + Array.Copy(curl.Rate, keyChunk, length); + + for (var n = 0; n < length; n++) + payload[payloadIndex + i + n] = + TritsHelper.Sum(payload[payloadIndex + i + n], (sbyte) -keyChunk[n]); + + curl.Absorb(payload, payloadIndex + i, length); + } + } + } +} \ No newline at end of file diff --git a/Src/IotaSharp.MAM/IotaSharp.MAM.csproj b/Src/IotaSharp.MAM/IotaSharp.MAM.csproj new file mode 100644 index 0000000..438bf5b --- /dev/null +++ b/Src/IotaSharp.MAM/IotaSharp.MAM.csproj @@ -0,0 +1,45 @@ + + + + net461;netstandard2.0 + $(LibraryFrameworks) + latest + 1.0.0.1 + 1.0.0.0 + 1.0.1 + IotaSharp + Iota + IotaSharp.MAM + Iota Library for .NET + Copyright © Iota 2008 + Iota Library for .NET + en-US + IotaSharp.MAM + IotaSharp.MAM + MAM + MIT + IotaSharp.MAM + IotaSharp.MAM + true + 2.12 + true + snupkg + false + + + + IotaSharp.MAM + + + IotaSharp.MAM .NET Standard 2.0 + + + + + + + + + + + diff --git a/Src/IotaSharp.MAM/Merkle/MerkleNode.cs b/Src/IotaSharp.MAM/Merkle/MerkleNode.cs new file mode 100644 index 0000000..5331f90 --- /dev/null +++ b/Src/IotaSharp.MAM/Merkle/MerkleNode.cs @@ -0,0 +1,80 @@ +using System; + +namespace IotaSharp.MAM.Merkle +{ + public class MerkleNode + { + public MerkleNode(MerkleNode left, sbyte[] hash, MerkleNode right) + { + Hash = hash; + LeftNode = left; + RightNode = right; + + if (LeftNode != null) + LeftNode.Parent = this; + + if (RightNode != null) + RightNode.Parent = this; + } + + public sbyte[] Hash { get; protected set; } + public MerkleNode LeftNode { get; protected set; } + public MerkleNode RightNode { get; protected set; } + public MerkleNode Parent { get; protected set; } + public bool IsLeaf => LeftNode == null && RightNode == null; + public bool IsFullNode => LeftNode != null && RightNode != null; + + public int Size + { + get + { + if (IsLeaf) + return 1; + + int leftSize = LeftNode?.Size ?? 0; + int rightSize = RightNode?.Size ?? 0; + + return 1 + leftSize + rightSize; + } + } + + public int Depth + { + get + { + if (IsLeaf) + return 1; + + if (LeftNode != null) + return 1 + LeftNode.Depth; + + if (LeftNode == null && RightNode != null) + throw new System.Exception("Error!"); + + return 0; + } + } + + public int Count + { + get + { + if (IsLeaf) + return 1; + + int leftCount = LeftNode?.Count ?? 0; + int rightCount = RightNode?.Count ?? 0; + + return leftCount + rightCount; + } + } + + public sbyte[] Slice() + { + sbyte[] clone = new sbyte[Hash.Length]; + Array.Copy(Hash, clone, Hash.Length); + return clone; + } + + } +} diff --git a/Src/IotaSharp.MAM/Merkle/MerkleSibling.cs b/Src/IotaSharp.MAM/Merkle/MerkleSibling.cs new file mode 100644 index 0000000..8579f77 --- /dev/null +++ b/Src/IotaSharp.MAM/Merkle/MerkleSibling.cs @@ -0,0 +1,71 @@ +using System; +using IotaSharp.MAM.Utils; + +namespace IotaSharp.MAM.Merkle +{ + /// + /// + /// + public class MerkleSibling + { + /// + /// + /// + /// + /// + public MerkleSibling(sbyte[] hash, MerkleSibling nextSibling) + { + Hash = hash; + NextSibling = nextSibling; + } + + /// + /// + /// + public sbyte[] Hash { get; protected set; } + + /// + /// + /// + public MerkleSibling NextSibling { get; protected set; } + + /// + /// + /// + public int Length + { + get + { + if (NextSibling == null) + return 1; + + return 1 + NextSibling.Length; + } + } + + /// + /// + /// + public sbyte[] Siblings + { + get + { + int tritsLength = Length * Constants.HashLength; + sbyte[] trits = new sbyte[tritsLength]; + + WriteBranch(tritsLength - Constants.HashLength, trits); + + return trits; + } + } + + private void WriteBranch(int index, sbyte[] trits) + { + Array.Copy(Hash, 0, trits, index, Constants.HashLength); + if (index != 0) + { + NextSibling.WriteBranch(index - Constants.HashLength, trits); + } + } + } +} diff --git a/Src/IotaSharp.MAM/Merkle/MerkleTree.cs b/Src/IotaSharp.MAM/Merkle/MerkleTree.cs new file mode 100644 index 0000000..b7630d6 --- /dev/null +++ b/Src/IotaSharp.MAM/Merkle/MerkleTree.cs @@ -0,0 +1,212 @@ +using System; +using IotaSharp.MAM.Utils; +using IotaSharp.Pow; + +namespace IotaSharp.MAM.Merkle +{ + public class MerkleTree + { + private static readonly sbyte[] NullHash = new sbyte[Constants.HashLength]; + + public static MerkleNode CreateMerkleTree( + sbyte[] seed, int index, uint count, + int security, + ICurl c1, + ICurl c2, + ICurl c3) + { + if (count == 0) + return null; + + if (count == 1) + return CreateMerkleLeaf(seed, index, security, c1, c2, c3); + + uint ct = NextPowerOfTwo(count); + return CreateMerkleNode(seed, index, count, ct, security, c1, c2, c3); + } + + public static MerkleSibling CreateMerkleBranch( + MerkleNode merkleTree, int index) + { + if (merkleTree == null || index >= merkleTree.Count) + throw new ArgumentOutOfRangeException(); + + if (merkleTree.IsLeaf) + return null; + + int leftCount = merkleTree.LeftNode.Count; + bool goLeft = index < leftCount; + + MerkleNode sibling; + MerkleNode child; + if (goLeft) + { + sibling = merkleTree.RightNode; + child = merkleTree.LeftNode; + } + else + { + sibling = merkleTree.LeftNode; + child = merkleTree.RightNode; + } + + sbyte[] hash = sibling.Slice(); + + if (!goLeft) + { + index = index - leftCount; + } + + MerkleSibling nextSibling = CreateMerkleBranch(child, index); + + return new MerkleSibling(hash, nextSibling); + } + + // root + public static int Root(sbyte[] address, sbyte[] hashes, int index, ICurl curl) + { + int i = 1; + int numBeforeEnd = 0; + sbyte[] outTrits = new sbyte[Constants.HashLength]; + Array.Copy(address, outTrits, Constants.HashLength); + + for (int n = 0; n < hashes.Length / Constants.HashLength; n++) + { + curl.Reset(); + if ((i & index) == 0) + { + numBeforeEnd += 1; + + curl.Absorb(outTrits); + curl.Absorb(hashes, n * Constants.HashLength, Constants.HashLength); + } + else + { + curl.Absorb(hashes, n * Constants.HashLength, Constants.HashLength); + curl.Absorb(outTrits); + } + + i <<= 1; + + Array.Copy(curl.Rate, outTrits, Constants.HashLength); + + } + + return numBeforeEnd; + } + + #region Private Method + + private static MerkleNode CreateMerkleNode( + sbyte[] seed, int index, + uint remainingCount, uint remainingWidth, + int security, + ICurl c1, ICurl c2, ICurl c3) + { + if (remainingCount == 0) + return null; + + if (remainingWidth == 0) + return null; + + if (remainingWidth == 1) + return CreateMerkleLeaf(seed, index, security, c1, c2, c3); + + return Combine(seed, index, remainingCount, remainingWidth, security, c1, c2, c3); + } + + private static MerkleNode Combine( + sbyte[] seed, int index, + uint count, uint remainingWidth, + int security, + ICurl c1, ICurl c2, ICurl c3) + { + uint rightCount = NextPowerOfTwo(count) >> 1; + uint leftCount = count - rightCount; + + var left = CreateMerkleNode(seed, index, leftCount, remainingWidth >> 1, security, c1, c2, c3); + var right = CreateMerkleNode(seed, (int) (index + leftCount), rightCount, remainingWidth >> 1, security, c1, + c2, + c3); + + if (left != null && (left.IsLeaf || left.IsFullNode)) + { + c1.Absorb(left.Hash); + } + else + { + c1.Absorb(NullHash); + } + + if (right != null && (right.IsLeaf || right.IsFullNode)) + { + c1.Absorb(right.Hash); + } + else + { + c1.Absorb(NullHash); + } + + sbyte[] hash = new sbyte[Constants.HashLength]; + c1.Squeeze(hash, 0, hash.Length); + c1.Reset(); + + return new MerkleNode(left, hash, right); + + } + + private static MerkleNode CreateMerkleLeaf( + sbyte[] seed, int index, + int security, + ICurl c1, ICurl c2, ICurl c3) + { + sbyte[] hash = ComputeHash(seed, index, security, c1, c2, c3); + + return new MerkleNode(null, hash, null); + } + + private static sbyte[] ComputeHash( + sbyte[] seed, int index, + int security, + ICurl c1, ICurl c2, ICurl c3) + { + sbyte[] subseed = HashHelper.Subseed(seed, index, c1); + + c1.Reset(); + + //iss::subseed_to_digest(&subseed, security, &mut hash, c1, c2, c3); + sbyte[] digest = HashHelper.SubseedToDigest(subseed, security, c1, c2, c3); + + c1.Reset(); + c2.Reset(); + c3.Reset(); + + //iss::address(&mut hash, c1); + sbyte[] address = HashHelper.Address(digest, c1); + + c1.Reset(); + + return address; + } + + private static uint NextPowerOfTwo(uint count) + { + int i = 0; + uint result = 0; + for (; i < 32; i++) + { + result = (uint) 1 << i; + if (count <= result) + break; + } + + if (i < 32) + return result; + + throw new ArgumentOutOfRangeException(); + + } + + #endregion + } +} diff --git a/Src/IotaSharp.MAM/Utils/BitwiseOperators.cs b/Src/IotaSharp.MAM/Utils/BitwiseOperators.cs new file mode 100644 index 0000000..92db3da --- /dev/null +++ b/Src/IotaSharp.MAM/Utils/BitwiseOperators.cs @@ -0,0 +1,15 @@ +namespace IotaSharp.MAM.Utils +{ + public static class BitwiseOperators + { + public static ulong RotateLeft(this ulong original, int bits) + { + return (original << bits) | (original >> (64 - bits)); + } + + public static ulong RotateRight(this ulong original, int bits) + { + return (original >> bits) | (original << (64 - bits)); + } + } +} diff --git a/Src/IotaSharp.MAM/Utils/Constants.cs b/Src/IotaSharp.MAM/Utils/Constants.cs new file mode 100644 index 0000000..2edfd3c --- /dev/null +++ b/Src/IotaSharp.MAM/Utils/Constants.cs @@ -0,0 +1,19 @@ +namespace IotaSharp.MAM.Utils +{ +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + public class Constants + { + public const int Radix = 3; + public const int TritsPerTryte = 3; + public const int HashLength = 243; + public const int TryteWidth = 3; + public const int MaxTryteValue = 13; + public const int MinTryteValue = -13; + public const int KeyLength = ((HashLength / 3) / Radix) * HashLength; + public const int DigestLength = HashLength; + public const int AddressLength = HashLength; + public const int SignatureLength = KeyLength; + public const int MessageNonceLength = HashLength / 3; + } +#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 +} diff --git a/Src/IotaSharp.MAM/Utils/HammingNonce.cs b/Src/IotaSharp.MAM/Utils/HammingNonce.cs new file mode 100644 index 0000000..abcdaf3 --- /dev/null +++ b/Src/IotaSharp.MAM/Utils/HammingNonce.cs @@ -0,0 +1,273 @@ +using System; +using IotaSharp.Pow; + +namespace IotaSharp.MAM.Utils +{ + /// Searches for a checksum given by hamming weight + /// Returns true if it found a valid nonce for the checksum. + /// It will start with a number of trits given by `length`, but may grow + /// If security is 1, then the first 81 trits will sum to 0 + /// If security is 2, then the first 81 trits will not sum to 0, but the first 162 trits will. + /// If security is 3, then neither the first 81 nor the first 162 trits will sum to zero, but + /// the entire hash will sum to zero + /// To prepare, you should absorb the length in trits + public class HammingNonce + { + private const int Radix = 3; + private const int NumberOfRoundsP81 = 81; + private const int NumberOfRoundsP27 = 27; + private readonly int _numberOfRounds; + + private const int CurlHashLength = 243; + private const int CurlStateLength = CurlHashLength * 3; + + private readonly ulong[] _stateHigh; + private readonly ulong[] _stateLow; + + public HammingNonce(SpongeFactory.Mode curlMode) + { + _stateLow = new ulong[CurlStateLength]; + _stateHigh = new ulong[CurlStateLength]; + + switch (curlMode) + { + case SpongeFactory.Mode.CURLP27: + _numberOfRounds = NumberOfRoundsP27; + break; + case SpongeFactory.Mode.CURLP81: + _numberOfRounds = NumberOfRoundsP81; + break; + } + } + + + public bool Search( + int security, int offset, int length, + ICurl tcurl) + { + // + SearchPrepareTrits(tcurl.State, offset); + + // + return SearchCpu(tcurl.State, offset, length, security); + + } + + private bool SearchCpu(sbyte[] state, int offset, int length, int security) + { + int count = Math.Min(length, CurlHashLength) - offset; + + // Ignore group incr + int index = -1; + while (index < 0) + { + int increaseLength = Increase(_stateLow, _stateHigh, offset + count * 2 / 3, count - count * 2 / 3); + count = + (int) Math.Min(TritsHelper.RoundThird(offset + count * 2 / 3 + increaseLength), CurlHashLength) - + offset; + + // clone + ulong[] stateLow = new ulong[CurlStateLength]; + ulong[] stateHigh = new ulong[CurlStateLength]; + Array.Copy(_stateLow, stateLow, CurlStateLength); + Array.Copy(_stateHigh, stateHigh, CurlStateLength); + + // transform + Transform(stateLow, stateHigh); + + // check + index = Check(security, stateLow, stateHigh); + } + + // output + for (int i = 0; i < count; i++) + { + ulong low = (_stateLow[i] >> index) & 1; + ulong high = (_stateHigh[i] >> index) & 1; + + if (low == 1 && high == 0) + state[i] = -1; + else if (low == 0 && high == 1) + state[i] = 1; + else if (low == 1 && high == 1) + state[i] = 0; + else + state[i] = 0; + } + + return false; + } + + private int Check(int security, ulong[] stateLow, ulong[] stateHigh) + { + sbyte[] hash = new sbyte[CurlHashLength]; + + for (int i = 0; i < 64; i++) + { + for (int n = 0; n < CurlHashLength; n++) + { + ulong low = (stateLow[n] >> i) & 1; + ulong high = (stateHigh[n] >> i) & 1; + + if (low == 1 && high == 0) + hash[n] = -1; + else if (low == 0 && high == 1) + hash[n] = 1; + else if (low == 1 && high == 1) + hash[n] = 0; + else + hash[n] = 0; + + } + + if (HashHelper.CheckSumSecurity(hash) == security) + return i; + + } + + return -1; + } + + private void Transform(ulong[] stateLow, ulong[] stateHigh) + { + var curlScratchpadLow = new ulong[CurlStateLength]; + var curlScratchpadHigh = new ulong[CurlStateLength]; + var curlScratchpadIndex = 0; + for (var round = _numberOfRounds; round-- > 0;) + { + Array.Copy(stateLow, 0, curlScratchpadLow, 0, CurlStateLength); + Array.Copy(stateHigh, 0, curlScratchpadHigh, 0, CurlStateLength); + for (var curlStateIndex = 0; curlStateIndex < CurlStateLength; curlStateIndex++) + { + var alpha = curlScratchpadLow[curlScratchpadIndex]; + var beta = curlScratchpadHigh[curlScratchpadIndex]; + var gamma = curlScratchpadHigh[curlScratchpadIndex += curlScratchpadIndex < 365 ? 364 : -365]; + var delta = (alpha | ~gamma) & (curlScratchpadLow[curlScratchpadIndex] ^ beta); + stateLow[curlStateIndex] = ~delta; + stateHigh[curlStateIndex] = (alpha ^ gamma) | delta; + } + } + } + + private int Increase(ulong[] stateLow, ulong[] stateHigh, int index, int length) + { + for (int i = 0; i < length; i++) + { + ulong low = stateLow[index + i]; + ulong high = stateHigh[index + i]; + + stateLow[index + i] = high ^ low; + stateHigh[index + i] = low; + + if ((high & (~low)) == 0) + return length; + } + + return length + 1; + } + + private void SearchPrepareTrits(sbyte[] state, int offset) + { + for (int i = 0; i < CurlStateLength; i++) + { + switch (state[i]) + { + case -1: + _stateLow[i] = ulong.MaxValue; + _stateHigh[i] = ulong.MinValue; + break; + case 0: + _stateLow[i] = ulong.MaxValue; + _stateHigh[i] = ulong.MaxValue; + break; + case 1: + _stateLow[i] = ulong.MinValue; + _stateHigh[i] = ulong.MaxValue; + break; + default: + throw new InvalidCastException($"Invalid Trit: {state[i]}"); + } + } + + Offset(offset, 0); + } + + private void Offset(int offset, int value) + { + int longSize = sizeof(long) * 8; + int numOffsetTrits = TritsOffset(longSize); + + int shift; + int max = (int) Math.Pow(Radix, numOffsetTrits); + + if (value < 0) + { + shift = max + value % max; + } + else + { + shift = value % max; + } + + ulong baseValue = Radix; + int inShift = 0; + + for (int i = 0; i < numOffsetTrits; i++) + { + int numExpanded = (int) Math.Pow(Radix, i + 1); + int outShift = numExpanded / Radix; + if (shift != 0) + { + int m = shift % numExpanded; + inShift += m; + shift -= m; + } + + if (inShift < longSize) + { + _stateHigh[offset + i] = (baseValue >> (inShift % numExpanded)); + } + + int j = numExpanded - inShift % numExpanded; + while (j < longSize) + { + _stateHigh[offset + i] |= baseValue.RotateLeft(j); + j += numExpanded; + } + + _stateLow[offset + i] = _stateHigh[offset + i] >> outShift; + + j -= outShift; + + int k = j; + if (j >= longSize) + k = j - numExpanded; + + if (k < 0) + { + _stateLow[offset + i] |= baseValue.RotateRight(-k); + } + else + { + _stateLow[offset + i] |= baseValue.RotateLeft(k); + } + + baseValue |= baseValue.RotateLeft(numExpanded * 2 / 3); + baseValue |= baseValue.RotateLeft(numExpanded / 3 * 2); + inShift += outShift; + } + } + + private int TritsOffset(int longSize) + { + // get the number of trits needed for a full set of offset binary coded trits + int o = 1; + while (Math.Pow(Radix, o) < longSize) + { + o++; + } + + return o; + } + } +} diff --git a/Src/IotaSharp.MAM/Utils/HashHelper.cs b/Src/IotaSharp.MAM/Utils/HashHelper.cs new file mode 100644 index 0000000..f6bb64b --- /dev/null +++ b/Src/IotaSharp.MAM/Utils/HashHelper.cs @@ -0,0 +1,252 @@ +using System; +using IotaSharp.Pow; +using IotaSharp.Utils; + +namespace IotaSharp.MAM.Utils +{ + /// + /// + /// + public class HashHelper + { + /// + /// + /// + /// + /// + public static string Hash(string trytes) + { + Sponge curl = new Curl(SpongeFactory.Mode.CURLP81); + var trits = Converter.ToTrits(trytes); + var hashTrits = new sbyte[Sponge.HASH_LENGTH]; + + curl.Absorb(trits, 0, trits.Length); + curl.Squeeze(hashTrits, 0, Sponge.HASH_LENGTH); + curl.Reset(); + + return Converter.ToTrytes(hashTrits); + } + + /// + /// + /// + /// + /// + /// + /// + public static sbyte[] Subseed(sbyte[] seed, int index, ICurl curl) + { + sbyte[] subseed = new sbyte[Constants.HashLength]; + int copyLength = Math.Min(seed.Length, subseed.Length); + Array.Copy(seed, subseed, copyLength); + + for (var i = 0; i < index; i++) + for (var j = 0; j < Constants.HashLength; j++) + if (++subseed[j] > 1) + subseed[j] = -1; + else + break; + + curl.Absorb(subseed); // curl.absorb(&out[0..seed.len()]); + curl.Squeeze(subseed); // curl.squeeze(&mut out[0..HASH_LENGTH]); + curl.Reset(); + + return subseed; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static sbyte[] SubseedToDigest( + sbyte[] subseed, int security, + ICurl c1, ICurl c2, ICurl c3) + { + sbyte[] digest = new sbyte[Constants.HashLength]; + + int length = security * Constants.KeyLength / Constants.HashLength; + + c1.Absorb(subseed); + + for (int i = 0; i < length; i++) + { + c1.Squeeze(digest); + + for (int n = 0; n < 27; n++) + { + c2.Reset(); + c2.Absorb(digest); + Array.Copy(c2.State, digest, Constants.HashLength); + } + + c3.Absorb(digest); + } + + c3.Squeeze(digest); + + return digest; + } + + /// + /// + /// + /// + /// + /// + public static sbyte[] Address(sbyte[] digest, ICurl curl) + { + sbyte[] address = new sbyte[Constants.AddressLength]; + + curl.Absorb(digest); + curl.Squeeze(address); + curl.Reset(); + + return address; + } + + /// + /// + /// + /// + /// + /// + /// + /// + // Take first 243 trits of key_space as subseed, and write key out to key_space + public static void Key(sbyte[] keySpace, int index, int length, int security, ICurl curl) + { + int totalLength = security * Constants.KeyLength; + if (totalLength != length) + throw new ArgumentException("Key space size must be equal to security space size"); + + curl.Absorb(keySpace, index, Constants.HashLength); + curl.Squeeze(keySpace, index, length); + + for (int divOffset = 0; divOffset < length / Constants.HashLength; divOffset++) + { + int offset = divOffset * Constants.HashLength; + curl.Reset(); + curl.Absorb(keySpace, index + offset, Constants.HashLength); + + Array.Copy(curl.State, 0, keySpace, index + offset, Constants.HashLength); + } + + curl.Reset(); + + } + + /// + /// + /// + /// + /// + /// + /// + /// + //Takes a bundle and input key key_signature, and writes the signature out to key_signature + public static void Signature(sbyte[] bundle, sbyte[] keySignature, int index, int length, ICurl curl) + { + if (bundle.Length != Constants.HashLength) + throw new ArgumentException("bundle are not equal HashLength!"); + + int totalLength = Constants.KeyLength * CheckSumSecurity(bundle); + if (totalLength != length) + throw new ArgumentException("Key space size must be equal to security space size"); + + for (int i = 0; i < length / Constants.HashLength; i++) + { + int maxCount = Constants.MaxTryteValue - + (bundle[i * Constants.TryteWidth] + + bundle[i * Constants.TryteWidth + 1] * 3 + + bundle[i * Constants.TryteWidth + 2] * 9); + + for (int n = 0; n < maxCount; n++) + { + curl.Reset(); + curl.Absorb(keySignature, index + i * Constants.HashLength, Constants.HashLength); + Array.Copy(curl.State, 0, keySignature, index + i * Constants.HashLength, Constants.HashLength); + } + } + + curl.Reset(); + + } + + /// + /// + /// + /// + /// + public static int CheckSumSecurity(sbyte[] hash) + { + if (hash.Length != Constants.HashLength) + return 0; + + int sum = 0; + int end0 = Constants.HashLength / 3; + int end1 = end0 * 2; + + for (int i = 0; i < end0; i++) + sum += hash[i]; + if (sum == 0) + return 1; + + for (int i = end0; i < end1; i++) + sum += hash[i]; + if (sum == 0) + return 2; + + for (int i = end1; i < Constants.HashLength; i++) + sum += hash[i]; + if (sum == 0) + return 3; + + return 0; + + } + + /// + /// + /// + /// + /// + /// + /// + /// + // Takes an input `signature`, and writes its digest out to the first 243 trits + public static void DigestBundleSignature(sbyte[] bundle, sbyte[] keySignature, int index, int length, + ICurl curl) + { + if (bundle.Length != Constants.HashLength) + throw new ArgumentException("bundle are not equal HashLength!"); + + int totalLength = Constants.KeyLength * CheckSumSecurity(bundle); + if (totalLength != length) + throw new ArgumentException("Key space size must be equal to security space size"); + + for (int i = 0; i < length / Constants.HashLength; i++) + { + int maxCount = + (bundle[i * Constants.TryteWidth] + + bundle[i * Constants.TryteWidth + 1] * 3 + + bundle[i * Constants.TryteWidth + 2] * 9) - Constants.MinTryteValue; + + for (int n = 0; n < maxCount; n++) + { + curl.Reset(); + curl.Absorb(keySignature, index + i * Constants.HashLength, Constants.HashLength); + Array.Copy(curl.State, 0, keySignature, index + i * Constants.HashLength, Constants.HashLength); + } + } + + curl.Reset(); + + curl.Absorb(keySignature, index, length); + } + } +} \ No newline at end of file diff --git a/Src/IotaSharp.MAM/Utils/Pascal.cs b/Src/IotaSharp.MAM/Utils/Pascal.cs new file mode 100644 index 0000000..c1a307c --- /dev/null +++ b/Src/IotaSharp.MAM/Utils/Pascal.cs @@ -0,0 +1,130 @@ +using System; + +namespace IotaSharp.MAM.Utils +{ + public class Pascal + { + public static sbyte[] Zero = {1, 0, 0, -1}; + + public static Tuple Decode(sbyte[] trits) + { + // check Zero + ulong index; + for (index = 0; index < 4; index++) + { + if (trits[index] != Zero[index]) + break; + } + + if (index == 4) + return new Tuple(0, 4); + + // + var encodersStart = End(trits, 0); + var inputEnd = encodersStart + + PascalMinTrits((ulong) Math.Pow(2, (long) (encodersStart / Constants.TritsPerTryte)) - 1); + + var encoder = TritsHelper.Trits2Long(trits, (int) encodersStart, (int) (inputEnd - encodersStart)); + + long acc = 0; + int i = 0; + index = 0; + for (; index < encodersStart; index += Constants.TritsPerTryte, i++) + { + ulong left = encodersStart - index; + int length = left > Constants.TritsPerTryte ? Constants.TritsPerTryte : (int) left; + + long temp = TritsHelper.Trits2Long(trits, (int) index, length); + if (((encoder >> i) & 0x1L) != 0x0L) + { + temp = -temp; + } + + acc += (long) (Math.Pow(27, i)) * temp; + + } + + return new Tuple(acc, (int) inputEnd); + } + + public static int EncodedLength(long input) + { + if (input == 0) + return Zero.Length; + + int length = (int) TritsHelper.RoundThird((long) PascalMinTrits((ulong) Math.Abs(input))); + + return length + (int) PascalMinTrits((ulong) Math.Pow(2, (uint) (length / Constants.TritsPerTryte)) - 1); + } + + public static void Encode(long input, sbyte[] trits) + { + if (input == 0) + { + Array.Copy(Zero, trits, Zero.Length); + } + else + { + int length = (int) TritsHelper.RoundThird((long) PascalMinTrits((ulong) Math.Abs(input))); + long encoding = 0; + TritsHelper.Long2Trits(input, trits); + int index = 0; + + int end = length - Constants.TritsPerTryte; + for (int i = 0; i < end; i += Constants.TritsPerTryte) + { + int n = end - i > Constants.TritsPerTryte ? Constants.TritsPerTryte : end - i; + if (TritsHelper.Trits2Long(trits, i, n) > 0) + { + encoding |= (1L << index); + for (int j = 0; j < n; j++) + { + trits[i + j] = (sbyte) -trits[i + j]; + } + } + + index += 1; + } + + if (TritsHelper.Trits2Long(trits, length - Constants.TritsPerTryte, Constants.TritsPerTryte) < 0) + { + encoding |= (1L << index); + for (int j = 0; j < Constants.TritsPerTryte; j++) + { + trits[length - Constants.TritsPerTryte + j] = + (sbyte) -trits[length - Constants.TritsPerTryte + j]; + } + } + + sbyte[] encodingTrits = new sbyte[trits.Length - length]; + TritsHelper.Long2Trits(encoding, encodingTrits); + + Array.Copy(encodingTrits, 0, trits, length, encodingTrits.Length); + } + } + + private static ulong End(sbyte[] trits, int index) + { + if (TritsHelper.Trits2Long(trits, index, Constants.TritsPerTryte) > 0) + { + return Constants.TritsPerTryte; + } + + return Constants.TritsPerTryte + End(trits, index + Constants.TritsPerTryte); + } + + private static ulong PascalMinTrits(ulong input) + { + return MinTritsHelper(input, 1); + } + + private static ulong MinTritsHelper(ulong input, ulong baseNum) + { + if (input <= baseNum) + return 1; + + return 1 + MinTritsHelper(input, 1 + baseNum * Constants.Radix); + + } + } +} diff --git a/Src/IotaSharp.MAM/Utils/TritsHelper.cs b/Src/IotaSharp.MAM/Utils/TritsHelper.cs new file mode 100644 index 0000000..f1453f9 --- /dev/null +++ b/Src/IotaSharp.MAM/Utils/TritsHelper.cs @@ -0,0 +1,85 @@ +using System; + +namespace IotaSharp.MAM.Utils +{ + public class TritsHelper + { + public static long Trits2Long(sbyte[] trits, int index, int length) + { + long value = 0; + + for (var i = index + length - 1; i >= index; i--) + { + value = value * 3 + trits[i]; + } + + return value; + } + + public static ulong Long2Trits(long input, sbyte[] trits) + { + ulong end = WriteTrits(Math.Abs(input), trits); + if (input < 0) + { + for (int i = 0; i < trits.Length; i++) + { + trits[i] = (sbyte) -trits[i]; + } + } + + return end; + } + + private static ulong WriteTrits(long input, sbyte[] trits) + { + ulong index = 0; + long abs = input; + + while (true) + { + if (abs == 0) + break; + + var r = (sbyte) (abs % Constants.Radix); + abs = abs / Constants.Radix; + + if (r > 1) + { + abs += 1; + r = -1; + } + + trits[index] = r; + index++; + + } + + return index; + } + + public static long RoundThird(long input) + { + long rem = input % Constants.TritsPerTryte; + + if (rem == 0) + return input; + + return input + Constants.TritsPerTryte - rem; + + } + + public static sbyte Sum(sbyte i, sbyte j) + { + sbyte s = (sbyte)(i + j); + switch (s) + { + case -2: + return 1; + case 2: + return -1; + default: + return s; + } + } + } +} diff --git a/Src/IotaSharp.Tests/IotaAPITest.cs b/Src/IotaSharp.Tests/IotaAPITest.cs new file mode 100644 index 0000000..4dfd498 --- /dev/null +++ b/Src/IotaSharp.Tests/IotaAPITest.cs @@ -0,0 +1,492 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using IotaSharp.Core; +using IotaSharp.Exception; +using IotaSharp.Model; +using IotaSharp.Pow; +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.Tests +{ + + [TestClass] + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class IotaAPITest + { + private static IotaAPI _iotaApi; + + // Contains 6000 iota + private const string TEST_SEED1 = + "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; + + // Empty + private const string TEST_SEED2 = + "KHFKUYFYITYPJHFKAURUZAQKGVJEREFDJMYTAGHFEGPZ9GJWTEJGF9IHFUPOZNQLSNMFDSQOTHGPEJGKD"; + + // contains 1000 iota + private const string TEST_SEED3 = + "9JFTUEPOTYPJHFKAURUZAQKGVJEREFDJMYTAGHFEGPZ9GJWTEJGF9IHFUPOZNQLSNMFDJOEMFLLSDKGJD"; + + private const string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_1 = + "MALAZGDVZIAQQRTNYJDSZMY9VE9LAHQKTVCUOAGZUCX9IBUMODFFTMGUIUAXGLWZQ9CYRSLYBM9QBIBYAEIAOPKXEA"; + + private const string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2 = + "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC"; + + private const string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_3 = + "ASCZZOBQDMNHLELQKWJBMRETMHBTF9V9TNKYDIFW9PDXPUHPVVGHMSWPVMNJHSJF99QFCMNTPCPGS9DT9XAFKJVO9X"; + + private const string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_1 = + "MALAZGDVZIAQQRTNYJDSZMY9VE9LAHQKTVCUOAGZUCX9IBUMODFFTMGUIUAXGLWZQ9CYRSLYBM9QBIBYA"; + + private const string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2 = + "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZC"; + + private const string TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_3 = + "ASCZZOBQDMNHLELQKWJBMRETMHBTF9V9TNKYDIFW9PDXPUHPVVGHMSWPVMNJHSJF99QFCMNTPCPGS9DT9"; + + private const string TEST_HASH = + "PXAOYSAMBOOVTZTCXL9CPLEOHS9EFHAGOESSEZSOTIDPGBERPFPTSFW9SKEL9RKPPFYDPCIMRIPAZ9999"; + + //Has non-0 trits in the value area which exceeds max IOTA supply + private const string TEST_INVALID_TRYTES = + "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"; + + private const string TEST_TRYTES = + "CCWCXCGDEAXCGDEAPCEAHDTCGDHDRAADTCGDGDPCVCTC9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999WITPXKWONPNUPBESJTXQTZTFXFSTDUWVGYHW9VRDULWGBKDVAAJLOSAEPUSCMSOIYLKMIZLPEKAOYAXMWFLDKQJI99999999999999999999UUC9999999999999999FUCK9YOUIVBUGXD99999999999B99999999IDPWGXASJFLLGCDGPQVXYGSNUESCZQCEKVREGLZX9FCYQVUWESEKWSMHZTGMJLGBOLKU9GILFITSJLZBWEHH9RSRNBPSKKUZBBJDSYHYWYHLJUOAFCKMXGTRFTZHKKWSVKGRHHJECSILLLZXKYJAYAQYEOINSZ9999OCYREQNINOB9XMLJOXMDFJDTXYO9PANNXQSW9HPFAAHEPTSPHTNEBOFFNRPKSUQNTKSACROOJPXF99999UUC9999999999999999FUCK9YOUPNMHCEJIE999999999L99999999IGMVBPROUATGVGQTHYIYFVQETRW"; + + private const string TEST_MESSAGE = "JUSTANOTHERIOTATEST"; + private const string TEST_TAG = "IOTASHARPSPAM99999999999999"; + + private const int MIN_WEIGHT_MAGNITUDE = 14; + private const int MIN_WEIGHT_MAGNITUDE_DEV = 9; + + private const int DEPTH = 9; + private const int DEPTH_DEV = 4; + + private static readonly string[] TEST_ADDRESSES = + { + "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC", + "P9UDUZMN9DEXCRQEKLJYSBSBZFCHOBPJSDKMLCCVJDOVOFDWMNBZRIRRZJGINOUMPJBMYYZEGRTIDUABDODCNSCYJD", + "MIMVJEYREIIZLXOXQROMPJFCIX9NFVXD9ZQMNZERPI9OJIJFUWQ9WCTMKXEEPHYPWEZPHLJBJOFH9YTRBGCCKGIWYW", + "FOJHXRVJRFMJTFDUWJYYZXCZIJXKQALLXMLKHZFDMHWTIBBXUKSNSUYJLKYRQBNXKRSUXZHDTPWXYD9YFHA9AWOMPX", + "B9YNPQO9EXID9RDEEGLCBJBYKBLWHTOQOZKTLJDFPJZOPKJJTNUYUVVTDJPBCBYIWGPSCMNRZFGFHFSXHTIYXWAKZ9", + "NQEFOAFIYKZOUXDFQ9X9PHCNSDETRTJZINZ9EYGKU99QJLDSTSC9VTBAA9FHLNLNYQXWLTNPRJDWCGIPPYIPAMUSVY", + "CEGLBSXDJVXGKGOUHRGMAQDRVYXCQLXBKUDWKFFSIABCUYRATFPTEEDIFYGAASKFZYREHLBIXBTKP9KLCRTXEGJXKX", + "QLOXU9GIQXPPE9UUT9DSIDSIESRIXMTGZJMKLSJTNBCRELAVLWVJLUOLKGFCWAEPEQWZWPBV9YZJJEHUSMBQHBROEZ", + "XIRMYJSGQXMM9YPHJVVLAVGBBLEEMOOKHHBFWKEAXJFONZLNSLBCGPQEVDMMOGHFVRDSYTETIFOIVNCR9IUZLVJVWX", + "PDVVBYBXMHZKADPAYOKQNDPHRSWTHAWQ9GRVIBOIMZQTYCWEPCDWDVRSOUNASVBDLBOAMVLYEVVCMAM9NPLXNSO9BD", + "U9GAIAPUUQWJGISAZWPLHUELTZ9WSHWXS9JLPKOWHRRIVUKGWCTJMBULVMKTETTUNHZ9HWHBALUCJIROUBRIIYCUHC", + "VFPMKZLLMDUOEKNBEKQZPTNZJZF9UHRWSTHXLWQQ9OAXTZQHTZPAWNJNXKAZFSDFWKFQEKZIGJTLWQFLOBXMPLSQNB", + "IGHK9XIWOAYBZUEZHQLEXBPTXSWVANIOUZZCPNKUIJIJOJNAQCJWUJHYKCZOIKVAAHDGAWJZKLTPVQL9GJSCYKNWTW", + "LXQPWMNXSUZTEYNC9ZBBFHY9YWCCOVKBNIIOUSVXZJZMJKJFDUWGUVXYCHGKUHEEIDHSGEWFAHVJPRIJTJCNU9GJAC", + "AKFDX9PGGQLZUWRMZ9YBDF9CG9TWXCNALCSXSAWHFIMGXCSYCJLSWIQDGGVDRMNEKKECQEYAITGNLNJFQCFALBLRZX", + "YX9QSPYMSFVOW9UVZRDVOCPYYMUTDHCCPKHMXQSJQJYIXVCHILKW9GBYJTYGLIKBTRQMDCYBMLLNGSSIKNQOHMSKTD", + "DSYCJKNG9TAGJHSKZQ9XLKAKNSKJFZIPVEDGJFXRTFGENHZFQGXHWDBNXLLDABDMOYELPG9DIXSNJFWARNURMPPVKC", + "9ANNACZYLDDPZILLQBQG9YMG9XJUMTAENDFQ9HMSSEFWYOAXPJTUXBFTSAXDJPAO9FKTWBBSCSFMOUR9IDQNHAFE9W", + "WDTFFXHBHMFQQVXQLBFJFVVHVIIAVYM9PFAZCHMKET9ESMHIRHSMVDJBZTXPTAFVIASMSXRDCIYVWVQNODEVYEGMVW", + "XCCPS9GMTSUB9DXPVKLTBDHOFX9PJMBYZQYQEXMRQDPGQPLWRGZGXODYJKGVFOHHYUJRCSXAIDGYSAWRBRXZJONAYW", + "KVEBCGMEOPDPRCQBPIEMZTTXYBURGZVNH9PLHKPMM9D9FUKWIGLKZROGNSYIFHULLWQWXCNAW9HKKVIDCTGKHAVBJZ" + }; + + private const string Provider = "http://node05.iotatoken.nl:16265"; + private const string DevNetProvider = "https://nodes.devnet.thetangle.org:443"; + + [TestInitialize] + public void CreateProxyInstance() + { + _iotaApi = new IotaAPI() + { + IotaClient = new IotaClient(DevNetProvider), + LocalPoW = new PearlDiverLocalPoW() + }; + } + + [TestMethod] + public void ShouldGetInputs() + { + // Address 0 should contain 1000 + var res = _iotaApi.GetInputs(TEST_SEED1, 2, 0, 5, 0); + + Assert.IsNotNull(res, "Error on getInputs should have thrown"); + Assert.IsTrue(res.TotalBalance > 0, "Res should have a balance(8)"); + Assert.IsNotNull(res.Inputs, "Error on getInputs should have thrown"); + } + + [TestMethod] + public void ShouldCreateANewAddressWithChecksum() + { + var addressRequest = new AddressRequest(TEST_SEED1, 1) + { + Checksum = true, + Amount = 5 + }; + + GetNewAddressResponse res1 = _iotaApi.GetAddressesUnchecked(addressRequest); + Assert.AreEqual(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_1, res1.Addresses[0]); + + AddressRequest secondAddressRequest = new AddressRequest(TEST_SEED1, 2) + { + Checksum = true, + Amount = 5 + }; + + GetNewAddressResponse res2 = _iotaApi.GetAddressesUnchecked(secondAddressRequest); + Assert.AreEqual(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, res2.Addresses[0]); + + AddressRequest thirdAddressRequest = new AddressRequest(TEST_SEED1, 3) + { + Checksum = true, + Amount = 5 + }; + GetNewAddressResponse res3 = _iotaApi.GetAddressesUnchecked(thirdAddressRequest); + Assert.AreEqual(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_3, res3.Addresses[0]); + } + + [TestMethod] + public void ShouldCreateANewAddressWithoutChecksum() + { + var addressRequest = new AddressRequest(TEST_SEED1, 1) + { + Amount = 5 + }; + var res1 = _iotaApi.GetAddressesUnchecked(addressRequest); + Assert.AreEqual(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_1, res1.Addresses[0]); + + var secondAddressRequest = new AddressRequest(TEST_SEED1, 2) + { + Amount = 5 + }; + var res2 = _iotaApi.GetAddressesUnchecked(secondAddressRequest); + Assert.AreEqual(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2, res2.Addresses[0]); + + AddressRequest thirdAddressRequest = new AddressRequest(TEST_SEED1, 3) + { + Amount = 5 + }; + var res3 = _iotaApi.GetAddressesUnchecked(thirdAddressRequest); + Assert.AreEqual(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_3, res3.Addresses[0]); + } + + + [TestMethod] + public void ShouldCreate100Addresses() + { + AddressRequest addressRequest = new AddressRequest(TEST_SEED1, 2) + { + Amount = 100 + }; + GetNewAddressResponse res = _iotaApi.GetAddressesUnchecked(addressRequest); + Assert.AreEqual(100, res.Addresses.Count); + } + + [TestMethod] + public void GenerateNewAddressesWithZeroIndexAndZeroAmountShouldGenerateOneAddresses() + { + AddressRequest addressRequest = new AddressRequest(TEST_SEED1, 2) {Amount = 0}; + GetNewAddressResponse addressResponse = _iotaApi.GenerateNewAddresses(addressRequest); + Assert.AreEqual(1, addressResponse.Addresses.Count); + } + + [TestMethod] + public void GenerateNewAddressesWithZeroAmountShouldGenerateOneAddresses() + { + AddressRequest addressRequest = new AddressRequest(TEST_SEED1, 2) {Amount = 0, Index = 1}; + GetNewAddressResponse addressResponse = _iotaApi.GenerateNewAddresses(addressRequest); + Assert.AreEqual(1, addressResponse.Addresses.Count); + } + + [TestMethod] + public void GenerateNewAddresses() + { + AddressRequest addressRequest = new AddressRequest(TEST_SEED1, 2) {Amount = 1}; + GetNewAddressResponse firstAddressResponse = _iotaApi.GenerateNewAddresses(addressRequest); + Assert.AreEqual(1, firstAddressResponse.Addresses.Count); + Assert.IsNotNull(firstAddressResponse.Addresses[0]); + } + + [TestMethod] + public void GenerateNewAddressesWithSameIndexAndOneAmountShouldGenerateSameAddress() + { + AddressRequest addressRequest = new AddressRequest(TEST_SEED1, 2) {Amount = 1}; + GetNewAddressResponse firstAddressResponse = _iotaApi.GenerateNewAddresses(addressRequest); + GetNewAddressResponse secondAddressResponse = _iotaApi.GenerateNewAddresses(addressRequest); + Assert.AreEqual(1, firstAddressResponse.Addresses.Count); + Assert.AreEqual(1, secondAddressResponse.Addresses.Count); + Assert.AreEqual(firstAddressResponse.Addresses[0], secondAddressResponse.Addresses[0]); + } + + [TestMethod] + public void ShouldPrepareTransfer() + { + List transfers = new List + { + new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 5, TEST_MESSAGE, TEST_TAG) + }; + + List trytes = + _iotaApi.PrepareTransfers(TEST_SEED1, 2, transfers.ToArray(), null, null, null, false); + + Assert.IsNotNull(trytes, "prepareTransfers should throw an error on failure"); + Assert.IsFalse(trytes.Count == 0, "prepareTransfers should throw an error on failure"); + + Transaction first = new Transaction(trytes[0]); + Assert.AreEqual(first.LastIndex, first.CurrentIndex, + "prepareTransfers should have reversed bundle order for attachToTangle"); + } + + [TestMethod] + public void ShouldPrepareTransferWithInputs() + { + List transfers = new List(); + + GetBalancesAndFormatResponse rsp = _iotaApi.GetInputs(TEST_SEED1, 2, 0, 10, 0); + + List inputList = new List(rsp.Inputs); + + transfers.Add(new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 1, TEST_MESSAGE, TEST_TAG)); + List trytes = _iotaApi.PrepareTransfers(TEST_SEED1, 2, transfers.ToArray(), null, + inputList.ToArray(), null, true); + + Assert.IsNotNull(trytes, "prepareTransfers should throw an error on failure"); + Assert.IsFalse(trytes.Count == 0, "prepareTransfers should throw an error on failure"); + + Transaction first = new Transaction(trytes[0]); + Assert.AreEqual(first.LastIndex, first.CurrentIndex, + "prepareTransfers should have reversed bundle order for attachToTangle"); + } + + [TestMethod] + public void ShouldFailTransfer() + { + try + { + List transfers = new List + { + new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 100, TEST_MESSAGE, TEST_TAG) + }; + + _iotaApi.PrepareTransfers(TEST_SEED2, 2, transfers.ToArray(), null, null, null, false); + + Assert.Fail("prepareTransfers should have thrown an error due to lack of balance on the seed"); + } + catch (IllegalStateException e) + { + Assert.AreEqual(Constants.NOT_ENOUGH_BALANCE_ERROR, e.Message, + "Message should say that there is not enough balance"); + } + } + + [TestMethod] + public void ShouldFailTransferWithInputs() + { + try + { + List transfers = new List + { + new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 1, TEST_MESSAGE, TEST_TAG) + }; + _iotaApi.PrepareTransfers(TEST_SEED2, 2, transfers.ToArray(), null, Array.Empty(), null, true); + + Assert.Fail("prepareTransfer should have thrown an error on wrong/lack of inputs"); + } + catch (ArgumentException e) + { + Assert.AreEqual(Constants.INVALID_ADDRESSES_INPUT_ERROR, e.Message, + "Message should say that the input is invalid"); + } + } + + [TestMethod] + public void ShouldGetLastInclusionState() + { + var iotaApi = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + + GetInclusionStatesResponse res = iotaApi.GetLatestInclusion(new[] + { + TEST_HASH + }); + + Assert.IsNotNull(res.States, "States should be an array of booleans"); + Assert.IsTrue(res.States[0], "Hash should have been seen as confirmed"); + } + + [TestMethod] + public void ShouldFindTransactionObjects() + { + List ftr = _iotaApi.FindTransactionObjectsByAddresses(TEST_ADDRESSES); + Assert.IsNotNull(ftr, "findTransactionObjectsByAddresses should not return null on failure"); + + Assert.IsFalse(ftr.Count == 0, "findTransactionObjectsByAddresses should find multiple transactions"); + } + + //[TestMethod] + //public void ShouldGetAccountData() + //{ + // GetAccountDataResponse gad = _iotaApi.GetAccountData(TEST_SEED3, 2, 0, true, 0, true, 0, 10, true, 0); + // assertThat("GetAccountDataResponse should not return null on failure", gad, IsNull.notNullValue()); + //} + + [TestMethod] + public void ShouldNotGetBundle() + { + Assert.ThrowsException(() => { _iotaApi.GetBundle("SADASD"); }); + } + + [TestMethod] + public void ShouldGetBundle() + { + var iotaApi = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + + GetBundleResponse gbr = iotaApi.GetBundle(TEST_HASH); + Assert.IsNotNull(gbr, "GetBundleResponse should not return null on failure"); + } + + [TestMethod] + public void ShouldCheckConsistency() + { + GetNodeInfoResponse gni = _iotaApi.IotaClient.GetNodeInfo(); + CheckConsistencyResponse ccr = _iotaApi.IotaClient.CheckConsistency(gni.LatestSolidSubtangleMilestone); + + Assert.IsNotNull(ccr, "CheckConsistencyResponse should not return null on failure"); + Assert.IsTrue(ccr.State, "Latest milestone should always be consistent"); + } + + + [TestMethod] + public void ShouldGetTransfers() + { + var iotaApi = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + + GetTransferResponse gtr = iotaApi.GetTransfers(TEST_SEED3, 2, 0, 10, false); + Assert.IsNotNull(gtr.TransferBundles, "GetTransfers should return GetTransferResponse object on success"); + Assert.IsTrue(gtr.TransferBundles.Length > 0, "GetTransfers should return more than 0 transfers"); + } + + [TestMethod] + public void ShouldReplayBundle() + { + var iotaApi = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + + ReplayBundleResponse rbr = iotaApi.ReplayBundle(TEST_HASH, DEPTH, MIN_WEIGHT_MAGNITUDE, null); + Assert.IsNotNull(rbr, "Bundle should be replayed"); + } + + [TestMethod] + public void ShouldNotSendTrytes() + { + Assert.ThrowsException(() => + { + _iotaApi.SendTrytes(new[] {TEST_INVALID_TRYTES}, DEPTH, MIN_WEIGHT_MAGNITUDE_DEV, null); + }); + } + + [TestMethod] + public void ShouldGetTrytes() + { + GetTrytesResponse trytes = _iotaApi.IotaClient.GetTrytes(TEST_HASH); + Assert.IsNotNull(trytes); + Assert.AreEqual(1, trytes.Trytes.Count, "getTrytes should send back 1 transaction trytes"); + } + + [TestMethod] + public void ShouldFailBeforeSnapshotTimeStamp() + { + var iotaApi = new IotaAPI + { + IotaClient = new IotaClient(Provider) + }; + + Assert.ThrowsException(() => { iotaApi.BroadcastAndStore(TEST_TRYTES); }, + "Transaction did not fail on old timestamp value"); + } + + [TestMethod] + public void ShouldSendTrytes() + { + var response = + _iotaApi.SendTrytes(new[] {TEST_TRYTES}, DEPTH_DEV, MIN_WEIGHT_MAGNITUDE_DEV, null); + Assert.AreEqual(1, response.Length, "Sending 1 transaction received unexpected amount"); + } + + [TestMethod] + public void ShouldNotSendTransfer() + { + try + { + List transfers = new List + { + // Address is spent + new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 2, TEST_MESSAGE, TEST_TAG) + }; + + _iotaApi.SendTransfer(TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE_DEV, transfers.ToArray(), null, null, + false, true, + null); + Assert.Fail("Transaction did not fail on spent address"); + } + catch (ArgumentException e) + { + Assert.AreEqual(Constants.SENDING_TO_USED_ADDRESS_ERROR, e.Message, + "Message should say we try to use a used address"); + } + } + + [TestMethod] + public void ShouldSendTransferWithoutInputs() + { + List transfers = new List(); + + AddressRequest addressRequest = new AddressRequest(TEST_SEED1, 2) {Checksum = true}; + string address = _iotaApi.GenerateNewAddresses(addressRequest).Addresses[0]; + transfers.Add(new Transfer(address, 1, TEST_MESSAGE, TEST_TAG)); + + SendTransferResponse str = _iotaApi.SendTransfer(TEST_SEED1, 2, DEPTH_DEV, MIN_WEIGHT_MAGNITUDE_DEV, + transfers.ToArray(), + null, null, false, true, null); + + Assert.IsNotNull(str.Results, "Sending transfer should have returned multiple transactions"); + + Assert.AreEqual(0, str.Results[0].Item1.CurrentIndex, "Returned transfers should have normal bundle order"); + } + + [Ignore] + [TestMethod] + public void ShouldSendTransferWithInputs() + { + List inputList = new List(); + List transfers = new List(); + + GetBalancesAndFormatResponse rsp = _iotaApi.GetInputs(TEST_SEED3, 2, 0, 0, 1); + + //TODO(guojiancong): rep.Inputs.Count == 0 ??? + inputList.AddRange(rsp.Inputs); + + AddressRequest addressRequest = new AddressRequest(TEST_SEED3, 2) {Checksum = true}; + string address = _iotaApi.GenerateNewAddresses(addressRequest).Addresses[0]; + transfers.Add(new Transfer(address, 1, TEST_MESSAGE, TEST_TAG)); + + // validate Inputs to true would mean we have to spent all balance in once. Now we double spent but its devnet + SendTransferResponse str = _iotaApi.SendTransfer(TEST_SEED3, 2, DEPTH_DEV, MIN_WEIGHT_MAGNITUDE_DEV, + transfers.ToArray(), + inputList.ToArray(), null, false, true, null); + + Assert.IsNotNull(str.Results, "Sending transfer should have returned multiple transactions"); + } + } +} diff --git a/Src/IotaSharp.Tests/IotaClientTest.cs b/Src/IotaSharp.Tests/IotaClientTest.cs new file mode 100644 index 0000000..0cfb3ce --- /dev/null +++ b/Src/IotaSharp.Tests/IotaClientTest.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.Tests +{ + [TestClass] + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class IotaClientTest + { + private const string TEST_BUNDLE = + "XZKJUUMQOYUQFKMWQZNTFMSS9FKJLOEV9DXXXWPMQRTNCOUSUQNTBIJTVORLOQPLYZOTMLFRHYKMTGZZU"; + + private const string TEST_ADDRESS_UNSPENT = + "D9UZTBEAT9DMZKMCPEKSBEOWPAUFWKOXWPO9LOHZVTE9HAVTAKHWAIXCJKDJFGUOBOULUFTJZKWTEKCHDAPJEJXEDD"; + + private const string TEST_ADDRESS_SPENT = + "9SEGQNQHFHCAI9QXTVGBGTIZQDV9RSCGCGPQSPLNCNN9DSENFMLTD9SETUSYZCYG9JYPIAMXFHNT9YRFZMMRCMESPB"; + + private const string TEST_ADDRESS_WITHOUT_CHECKSUM = + "YJNQ9EQWSXUMLFCIUZDCAJZSAXUQNZSY9AKKVYKKFBAAHRSTKSHUOCCFTQVPPASPGGC9YGNLDQNOUWCAW"; + + private const string TEST_ADDRESS_WITH_CHECKSUM = + "YJNQ9EQWSXUMLFCIUZDCAJZSAXUQNZSY9AKKVYKKFBAAHRSTKSHUOCCFTQVPPASPGGC9YGNLDQNOUWCAWGWIJNRJMX"; + + private const string TEST_HASH = + "OOAARHCXXCPMNZPUEYOOUIUCTWZSQGKNIECIKRBNUUJEVMLJAWGCXREXEQGNJUJKUXXQAWWAZYKB99999"; + + private const string TAG = "IOTA9TAG9999999999999999"; + + private static IotaClient _iotaClient; + + [TestInitialize] + public void CreateProxyInstance() + { + _iotaClient = new IotaClient("https://nodes.devnet.iota.org:443"); + } + + [TestMethod] + public void ShouldGetNodeInfo() + { + var nodeInfo = _iotaClient.GetNodeInfo(); + Assert.IsNotNull(nodeInfo.AppVersion); + Assert.IsNotNull(nodeInfo.AppName); + Assert.IsNotNull(nodeInfo.JreVersion); + Assert.IsNotNull(nodeInfo.JreAvailableProcessors); + Assert.IsNotNull(nodeInfo.JreFreeMemory); + Assert.IsNotNull(nodeInfo.JreMaxMemory); + Assert.IsNotNull(nodeInfo.JreTotalMemory); + Assert.IsNotNull(nodeInfo.LatestMilestone); + Assert.IsNotNull(nodeInfo.LatestMilestoneIndex); + Assert.IsNotNull(nodeInfo.LatestSolidSubtangleMilestone); + Assert.IsNotNull(nodeInfo.LatestSolidSubtangleMilestoneIndex); + Assert.IsNotNull(nodeInfo.Neighbors); + Assert.IsNotNull(nodeInfo.PacketsQueueSize); + Assert.IsNotNull(nodeInfo.Time); + Assert.IsNotNull(nodeInfo.Tips); + Assert.IsNotNull(nodeInfo.TransactionsToRequest); + } + + [Ignore] + [TestMethod] + public void ShouldGetNeighbors() + { + + } + + [Ignore] + [TestMethod] + public void ShouldAddNeighbors() + { + + } + + [Ignore] + [TestMethod] + public void ShouldRemoveNeighbors() + { + + } + + [TestMethod] + public void ShouldGetTips() + { + var tips = _iotaClient.GetTips(); + Assert.IsNotNull(tips); + } + + [TestMethod] + public void ShouldFindTransactionsByAddresses() + { + var trans = _iotaClient.FindTransactionsByAddresses(TEST_ADDRESS_WITH_CHECKSUM); + Assert.IsNotNull(trans.Hashes); + Assert.IsTrue(trans.Hashes.Count > 0); + } + + [TestMethod] + public void ShouldFindTransactionsByApprovees() + { + var trans = _iotaClient.FindTransactionsByApprovees(TEST_ADDRESS_WITHOUT_CHECKSUM); + Assert.IsNotNull(trans.Hashes); + } + + [TestMethod] + public void ShouldFindTransactionsByBundles() + { + var trans = _iotaClient.FindTransactionsByBundles(TEST_HASH); + Assert.IsNotNull(trans.Hashes); + } + + [TestMethod] + public void ShouldFindTransactionsByTags() + { + var trans = _iotaClient.FindTransactionsByTags(TAG); + Assert.IsNotNull(trans.Hashes); + } + + [TestMethod] + public void ShouldGetTrytes() + { + var res = _iotaClient.GetTrytes(TEST_HASH); + Assert.IsNotNull(res.Trytes); + } + + [TestMethod] + public void ShouldNotGetInclusionStates() + { + var argumentException = Assert.ThrowsException(() => + { + _iotaClient.GetInclusionStates(new[] {TEST_HASH}, + new[] {"ZIJGAJ9AADLRPWNCYNNHUHRRAC9QOUDATEDQUMTNOTABUVRPTSTFQDGZKFYUUIE9ZEBIVCCXXXLKX9999"}); + }); + + Assert.IsTrue( + argumentException.Message.StartsWith("{\"error\":\"One of the tips is absent\",\"duration\":")); + } + + [TestMethod] + public void ShouldGetInclusionStates() + { + var res = + _iotaClient.GetInclusionStates( + new[] {TEST_HASH}, + new[] {_iotaClient.GetNodeInfo().LatestSolidSubtangleMilestone}); + Assert.IsNotNull(res.States); + } + + [TestMethod] // very long execution + public void ShouldGetTransactionsToApprove() + { + var res = _iotaClient.GetTransactionsToApprove(15); + Assert.IsNotNull(res.TrunkTransaction); + Assert.IsNotNull(res.BranchTransaction); + } + + [TestMethod] + public void ShouldInvalidDepth() + { + var argumentException = Assert.ThrowsException(() => + { + _iotaClient.GetTransactionsToApprove(17); + }); + + Assert.IsTrue( + argumentException.Message.StartsWith("{\"error\":\"Invalid depth input\",\"duration\":")); + } + + [TestMethod] + public void FindTransactionsWithValidTags() + { + var test = TEST_BUNDLE; + + var resp = _iotaClient.FindTransactions( + new[] {test}.ToList(), new[] {TAG}.ToList(), + new[] {test}.ToList(), new[] {test}.ToList()); + + Assert.IsNotNull(resp); + } + + [TestMethod] + public void FindTransactionsFailIfInvalidTagIsProvided() + { + var test = TEST_BUNDLE; + + var argumentException = Assert.ThrowsException(() => + { + _iotaClient.FindTransactions( + new[] {test}.ToList(), new[] {test}.ToList(), + new[] {test}.ToList(), new[] {test}.ToList()); + }); + + Assert.AreEqual("Invalid tag provided.", argumentException.Message); + } + + [TestMethod] + public void ShouldGetBalances() + { + var res = _iotaClient.GetBalances(100, new List {TEST_ADDRESS_WITH_CHECKSUM}, null); + Assert.IsNotNull(res.Balances); + Assert.IsNotNull(res.References); + Assert.IsNotNull(res.MilestoneIndex); + Assert.IsNotNull(res.Duration); + } + + [TestMethod] + public void AddressIsSpentFrom() + { + var ret = _iotaClient.WereAddressesSpentFrom(TEST_ADDRESS_SPENT); + Assert.IsTrue(ret.States[0]); + } + + [TestMethod] + public void AddressIsNotSpentFrom() + { + var ret = _iotaClient.WereAddressesSpentFrom(TEST_ADDRESS_UNSPENT); + Assert.IsFalse(ret.States[0]); + } + } +} diff --git a/Src/IotaSharp.Tests/IotaSharp.Tests.csproj b/Src/IotaSharp.Tests/IotaSharp.Tests.csproj new file mode 100644 index 0000000..93a7ebc --- /dev/null +++ b/Src/IotaSharp.Tests/IotaSharp.Tests.csproj @@ -0,0 +1,32 @@ + + + + net461;netcoreapp2.2 + $(LibraryFrameworks) + latest + IotaSharp.Tests + IotaSharp.Tests + false + False + Full + false + + + + + + + + + + + IotaSharp Tests + + + + + + IotaSharp Tests .NET Standard 2.0 + + + \ No newline at end of file diff --git a/Src/IotaSharp.Tests/Pow/KerlTest.cs b/Src/IotaSharp.Tests/Pow/KerlTest.cs new file mode 100644 index 0000000..4600ab6 --- /dev/null +++ b/Src/IotaSharp.Tests/Pow/KerlTest.cs @@ -0,0 +1,57 @@ +using System.Diagnostics.CodeAnalysis; +using IotaSharp.Pow; +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.Tests.Pow +{ + [TestClass] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class KerlTest + { + [TestMethod] + public void ShouldCreateValidHash1() + { + sbyte[] trits = + Converter.ToTrits("GYOMKVTSNHVJNCNFBBAH9AAMXLPLLLROQY99QN9DLSJUHDPBLCFFAIQXZA9BKMBJCYSFHFPXAHDWZFEIZ"); + Kerl kerl = (Kerl) SpongeFactory.Create(SpongeFactory.Mode.KERL); + kerl.Reset(); + kerl.Absorb(trits, 0, trits.Length); + sbyte[] hashTrits = new sbyte[trits.Length]; + kerl.Squeeze(hashTrits, 0, 243); + string hash = Converter.ToTrytes(hashTrits); + Assert.AreEqual(hash, "OXJCNFHUNAHWDLKKPELTBFUCVW9KLXKOGWERKTJXQMXTKFKNWNNXYD9DMJJABSEIONOSJTTEVKVDQEWTW"); + } + + [TestMethod] + public void ShouldCreateValidHash2() + { + sbyte[] trits = + Converter.ToTrits("9MIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJFGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH"); + Kerl kerl = (Kerl) SpongeFactory.Create(SpongeFactory.Mode.KERL); + kerl.Reset(); + kerl.Absorb(trits, 0, trits.Length); + sbyte[] hashTrits = new sbyte[trits.Length * 2]; + kerl.Squeeze(hashTrits, 0, 243 * 2); + string hash = Converter.ToTrytes(hashTrits); + Assert.AreEqual(hash, + "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA"); + } + + [TestMethod] + public void ShouldCreateValidHash3() + { + sbyte[] trits = + Converter.ToTrits( + "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA"); + Kerl kerl = (Kerl) SpongeFactory.Create(SpongeFactory.Mode.KERL); + kerl.Reset(); + kerl.Absorb(trits, 0, trits.Length); + sbyte[] hashTrits = new sbyte[trits.Length]; + kerl.Squeeze(hashTrits, 0, 243 * 2); + string hash = Converter.ToTrytes(hashTrits); + Assert.AreEqual(hash, + "LUCKQVACOGBFYSPPVSSOXJEKNSQQRQKPZC9NXFSMQNRQCGGUL9OHVVKBDSKEQEBKXRNUJSRXYVHJTXBPDWQGNSCDCBAIRHAQCOWZEBSNHIJIGPZQITIBJQ9LNTDIBTCQ9EUWKHFLGFUVGGUWJONK9GBCDUIMAYMMQX"); + } + } +} diff --git a/Src/IotaSharp.Tests/Pow/LocalPoWTest.cs b/Src/IotaSharp.Tests/Pow/LocalPoWTest.cs new file mode 100644 index 0000000..21305f1 --- /dev/null +++ b/Src/IotaSharp.Tests/Pow/LocalPoWTest.cs @@ -0,0 +1,87 @@ +using System.Diagnostics.CodeAnalysis; +using IotaSharp.Core; +using IotaSharp.Model; +using IotaSharp.Pow; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.Tests.Pow +{ + [TestClass] + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class LocalPoWTest + { + private static IotaClient _iotaClient; + + private static PearlDiverLocalPoW _localPoW; + + private static IotaAPI _iotaApi; + + private const string TEST_SEED1 = + "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; + + private const string TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2 = + "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC"; + + private const string TEST_MESSAGE = "JUSTANOTHERJOTATEST"; + private const string TEST_TAG = "JOTASPAM9999999999999999999"; + private const int MIN_WEIGHT_MAGNITUDE = 14; + private const int DEPTH = 4; + + private static readonly string[] TEST_TRYTES_VALUE_TRANSFER = + { + "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999NNIWDJKXYXOYEHXQQBOPWSYYKUVNBLBJFRBCPZBTNOG9NJHLSOCANRRGACYCIOYQJATHYUIPOFXLV9URBUIA999999999999999999999999IOTA9ACCOUNTS9TRANSFER999999TQWPAD99C99999999C99999999RTAVSEEHIJEYJYHYPFHTLVQLGGHBBTBWJEWBUCKXWHNBEA9YIIKQOCDIKBLJOPUOKLBCGFDJX9CCQPRIWEQVFKIGQPKTDADIAHNVCCV9OCIAOSTQVDQVFY9YJRITGJJMRGVJPPWIEIPJRZTMTBNWPLMRFPUAHPY999EQVFKIGQPKTDADIAHNVCCV9OCIAOSTQVDQVFY9YJRITGJJMRGVJPPWIEIPJRZTMTBNWPLMRFPUAHPY999IOTA9ACCOUNTS9TRANSFER99999FMBUPTZNF999999999MMMMMMMMMCFQLIPUFO9APITWHAQECZUQ9ONB", + "YWGMDTHWBQBUYCLHRKSDLKEUYOLLGRUWOUDGHZBYLR9E9OOVOEEPGUAAVVWONYESBLZIYSQE9Y9BKY9IDHLOKRGKMWLULBV9YIEUXQDXOMYCBBZWPUXPRFORAXZDPYNRDVU9THLKNZKMBISYYCVRSCYBUOWLCXYTEX9AWFVLPTHMKXGLZGLWMSDJBKJYBFV9AYWYFADKOVVLYVV9UYRDGNCLFHUAGDOZHMRGABFDWJFGYLRCVJCARLADDRVJFTRUXI9ZIMYJRGV9WEHFPABRZSI9GPBHBEKVFXZOXZCLGXFHA9QHRAVLUZDZQBIAVKPNJTCCBZY9ERFTYGEQSNQVAYSQDMGTHLMZEGWOGXYTUCNUWCJOMVX9ZEJE9SO9BNUGBHLOJZPQBPPPARJUFIMF9NNOKURSTEWRFFKDZOH9PZEYJXYXWXZJVRAGCJSNNDGVXG9QJVEPC9BCDRWTNTZYVWMKTJHMYVQJNULSPWANGCUGBFDWZNNXWYNFLNZWLCHSDAUDYVRH9AAIIZZJPEYEPPFYZQGLUTAADGZDHPWZCHREYRHKVIBDNDBQBT9NEBAT9SRPGBKGQBVKNGFYHKNXGDFYPPT9LUYGREAPGCDZZCDCZQUU9KASZSUFLHYQIQRBLQ9PB9KCZVEXHB9MOAWILVHRJEQONOKRVGQPUDEAJPBWBZDRBHKVWODTVZZYGQMEZQFBIDLCDZIJT9HHENWWPTCTWIIKHVMLSHXAFMGKBBLPZXACENGMFLIRSAFLTSHIFSMSUNJSIGICDHGOWMT9PGDWAPDPTBOXEXQKGYFCPXKXIAJAZTXLR9VBLVMTFLVTBQYLQRUAJNLZZZIGDMQFGW9QSTIZCJCZUFIEXXMPINNJCEMGFWETKBBGBWXRXPCKJOCQMUPFAJWHVXETDGHCUONFFLCXNZZGRVJGUUDSZCRFBAGUOGREXQVWIXYQFRWCWUXENLKUIEGYMXYFPXXHGASEOGDNLJMENIWLQNXLGROXHNMVQKCTQSPPRLICRJ9NQR9RXINSBKLYYNIHENVFXGHAE9NFCJOXZZHLNIMZKMKIXQMSYXRYNME9IJRQOVSEWQRASU9TZBHNRXKTLE9GNUWSPCHXNRSPTLDYYKYDORH9FDRYRVLUWQEVUKHPKUEKZOOHBOS9APGMMMXFPPNOIMTSLPKBMOMT9SDRLBCUXNBQMTHFGYPWGRPXEWFILFWRSBKEBNXHFYPW9ZSHURXAEBAIKYNHXBZT9ZQCBCKWONKZEQSKIAOCXDUJAGGKINLYCNUVWBDJTCRSUOQBUSHDWFXBPYJQWSANITQOUWXIHTRXJZ9RASQXZQEQXQWQUOOMMCMWRAIKUVCUEQLPPUNBAUN9K9RUUXUTDURPDKCBQJHNRKBGUAPMUFSCUUZNBVOKOMQV9XNGHAMP9JN9MKSFZRUCCKPMLY9IME9BAZPFRHTUWLYE99YWTCALYZXQ9RSJGSSTHCSNQQLHENBJRCOVCRPANFVLOKOY9LOMPMXKR9PQS9UYXEALDZUHZ9RQSTXSXT9IKVLHYV9PDNXZMN9CNWCBYEUWTSPNNRMFULNPY9SDBLJBSRZGWCT9ZFEANEWGDADSMEBRSBZLPEGDLSHYEKBALWYGWQNJZOSXGNK9BPNBRUB9DQDJNCGWKAIZJOF9PTEGSULBFC9WELZAAGXUBFEPRWELFIM9SAZMBZQBBZXSVXXRU9VYEXI9QLCWEDMJYIOCJTCGFIYOUIOLSKPVFXUZQDKRBVQQPRGPV9JWYFMOMRXIVEVUTWI9ZKVPRWTWSLAFKTQQCWVOUYEHEXTPQZPKHXRGNFCXDOZHDDQOVWLYFNKERVGNZNOETBUGIDXCIHUBLUWDOJVKJXXQPDNPLLXHYDZMACKBRVOORHRJYYAHKFSCXXPRHLRUKCXXRYI9HSZSEPCQDWGYP9BYZXWPOAUXLNCERGYUACGALDJLCGHQZKLUBXRHBDBUFZLSXACIAWGBPNINLQET9AFMXKTLRZAQRWXRYZRWAKZHHBZJCOXLSYDVORZQPWUSJUHI9VP9BSNLYXKQQLUKPHKHOYARSIREBW9IGNBEXZXSXRHHPIRTAAULXDJX9UPTO9DXUUDNUBHCWSQYEZBYMQHCARUVIAOKAWIFDYYYL9EQZZWJGJWJPKOTWFMUGZDEMGFVOHHKVAGQPOWIL9DIQEIPVJL9DEUVKEMKRJDYDVACBRXNYSIHEV9HWQECNHHZBKWDVM9MY9IGBSEQSMSRMZNRZXHAGBKMRFCA999999999999999999999999999IOTA9ACCOUNTS9TRANSFER999999TQWPAD99B99999999C99999999RTAVSEEHIJEYJYHYPFHTLVQLGGHBBTBWJEWBUCKXWHNBEA9YIIKQOCDIKBLJOPUOKLBCGFDJX9CCQPRIWDAG9LCCSHVDLUDX9UTZIUAOA9BNAYOFAVKUKKWDJDZODLUK9NDMGCZBA9REFPMQSOMDFVWVMEIHU99999EQVFKIGQPKTDADIAHNVCCV9OCIAOSTQVDQVFY9YJRITGJJMRGVJPPWIEIPJRZTMTBNWPLMRFPUAHPY999IOTA9ACCOUNTS9TRANSFER99999NKCXPTZNF999999999MMMMMMMMMNMKXOMG9LYENZYYOXP9CAQKKPQG", + "RASZHIFYMGATRQXGMSJQWQHR9RGQMYJZGRRUMIP9PEJHWLJIFO9OLVVMVKZXSHUKXMQFROOKOEJJBKBWCCYIGCTSEESZFSFGRE9SFSDQJK9CBYWUCRIJTNGD9GBXYRZOIZADTRJEV9CTKFVLCKXEXYNILDBMWPCWMAOBLQAPITBCIKIVKJGAVPPTWJSWGDDIZZVKJVVWIHYDTZDPQN9KWLJIOUP9LNODIFQLINSYCPCGSS9KWXDYM9POIQSJOFRUDVPYDSKZVJIS9ZUGSRW9WMV9YVACFQAZ9XZTPDADMFETEAOTGVUJLEAGQZBQVASQZQOCUCKOZPSIISXYXUIIPNPXEOWPBSTENOEYZYHOQJQHCTWMWJIFFDZMSTIEYMBULUSBITXQFURTPTYQXOD9WNTNJRQIZGLZXGEFATGADUQF9XMPCY9QBX9RKDFJON9OMJVYDAWKWUHNMCSCDSYZVFDUAERISVTZVLQBO9JTZWOQHZYNNBBBIMCWKMSDSPDXDJDQCCHOX9MYTTJVWMJSWMJLSWOZGZSTAQXAKKFYMGXJPNECEMVEWRWOCY9LMSLRFOIHSIGFB9CZNOUMYOSABKBO9TVZUXGSLOWJM9GYI9ONSKFFOEMGDVYJXPHUDRJHYZYMKPOARWDEHCWJBWZY9LDKWYLYSTHGBFRNLTZVRMILKVD9GQE9FIBAGD9ECTHGCSRJ9PJWNWQQDBPGWRARRXQM9GWQPMERSBRUYHA9ZFNTQZOCYTYNEBO9YWNVQMIQDSLFCIJA9DMDBPLWZEKTIYFRXHXS9LMRAHKKOIJPFCNLFX9GCY9SWUFFAARADXHWIFLLKFAFUZLCMGVHAKPHWGEO9WMNNTUQRITWPBLJUCGTICCYUZJDNW9UUEWHKFBKCDNVVSBJCLNKZVSJXKYUQCDTHASLWRIHBUD9NCHOVYBTFJBHGBLIQRJQL9XQBNMXPEBIWUHDMPAXERPVRDKMCXXWHPPNKELOPBBVRNIPTSNIXJGPYDM9X9BSNNYIPVDAEONMLIUQRVWZPGS9NKOQPHEIGNFD9ZIWBSEQJIRBPSMQRTAXSJDBFQYHVKYKNLCRBJOR9LVBQOKXCHIQF9VQIOCDJEJUB9QQASHOIATPSLKTDATJZHKTQMYDLKTPDXOOXNRXXWUSJGOSSSEFDGRTNCDOSWFVFDULFCMAMYBKOEWKSLQTASADLVGHPUWFJRDONSVUAWPKCXMGYQP9BBTABENPCOC9EMAOBRSJVWLKMRHBJSZBANINRUTZINISDX9AIHIETRIDRN9XXVQWYKCQZ9ZOGVHGHURNPPAKPGMSGJKETOV99WKXIIYMJOZOVVVDBXFCUGTSXXEIGCNEVHFWBJQSQUIOXXDOAMDXEKZNWHAVZJYTMZHSSUSMLCECSYSRVWSCYHKORQHMRDZBBUNQKRYPEBMGPGBGBUUVTLZONUOZ9PWEAZNVTVLELGUVPX9FOFDKFZOCBE9RUHJSQGLSWZMWLQDHWBMRRDDJPIZBOEDPJONMC9UHWIOZYBJXZQJHTZCOORPWDFRWTOQAQDAFLCSUABRGAWFRJWKSSJJDWJIF9GNPQFVJOD9ZELCUXUUFBWXGFFIZ9RYRTNBCWQAASJAUMQIYKNPQPWCYYMKOZZLFHCUCVMJSA9QRMYFGXKHVJYLWNZDSQUNOUHTXWT9ETGSZ9ZTNPRBDURNTCBSATXOGGIJKCPCISBPZHFPNWBZEBOUVIDIXOKCZPTRFYPBSUJGWGJBINNWNPRSIIRSQISDR9HTHYOHVJBXFBTQLZXMPHE9KRXRMF9HSEMZCLIP9EIVWLLEFGJUNWGKQJMEDYXYZQD9NO9ICFQTDJUPCTP9BIPLYEJYHNYBLBWRFRX9GOGPQCYFEUKVNTYOH9QWDNPMQMVZRM9ULLBXMKSHNOLYRIFYLKCKPTMPIUUJWUVFGUKNZGZDWOFIZJH9OT9KSLJSZKWVYYYYHW9EPFRXOTEWNPAMOKFBLYDYXOV9R9RUEMDELTRXFTVNVZNBRJAPQWSOB9NXERDJYA99VLUKVS9VTQCRB9XYCYMWRSJQCGRW9WUKSUNKWZLNMCZNFTAAHML999WMZESFYED9AKDHRONUCKXUFWGDSBATTJCEEOKPCWIVVQQTTDALUXCLNXGSMGBBZZFJONZQIGLA9PYREAFNGGPFWHKGNWWDIQEIPVJL9DEUVKEMKRJDYDVACBRXNYSIHEV9HWQECNHHZBKWDVM9MY9IGBSEQSMSRMZNRZXHAGBKMRFCAERZ999999999999999999999999IOTA9ACCOUNTS9TRANSFER999999TQWPAD99A99999999C99999999RTAVSEEHIJEYJYHYPFHTLVQLGGHBBTBWJEWBUCKXWHNBEA9YIIKQOCDIKBLJOPUOKLBCGFDJX9CCQPRIWIJEXBNHMIBEWDCDFPGXRUDJGXDR9NXGEFHJJEOZPBZE9GSNYHDFBLTT9VMMFWFPFWYDBMBLNCSREZ9999EQVFKIGQPKTDADIAHNVCCV9OCIAOSTQVDQVFY9YJRITGJJMRGVJPPWIEIPJRZTMTBNWPLMRFPUAHPY999IOTA9ACCOUNTS9TRANSFER99999CTX9PTZNF999999999MMMMMMMMMIBYBANZPHARBAWTVQHJADRXLVZY", + "RBTCFDTCEAXCGDEAMDCDIDFDEAXCCDHDPCFA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999EWLWIKTWEIUBCMECMFHYVSLMMR9XLLABPFY99GSXAKLOAATIQQHSI9MY9TFWZBLB9EBQUGFOSCLC9MXBZA99999999999999999999999999GNUA9ACCOUNTS9TRANSFER999999TQWPAD99999999999C99999999RTAVSEEHIJEYJYHYPFHTLVQLGGHBBTBWJEWBUCKXWHNBEA9YIIKQOCDIKBLJOPUOKLBCGFDJX9CCQPRIWHBKOBLI9TAKAMIZOYISMAJVGKRVJNDCAM9EWGTWBBDHSJMRIBSSYS9DTYVBEOBPBDSFULPBJHLBOA9999EQVFKIGQPKTDADIAHNVCCV9OCIAOSTQVDQVFY9YJRITGJJMRGVJPPWIEIPJRZTMTBNWPLMRFPUAHPY999IOTA9ACCOUNTS9TRANSFER99999CP9BPTZNF999999999MMMMMMMMMGFFGWAGRRSN9CMWRXRLFFTZFBJG" + }; + + [TestInitialize] + public void CreateProxyInstance() + { + _iotaClient = new IotaClient("https://nodes.devnet.thetangle.org:443"); + _localPoW = new PearlDiverLocalPoW(); + + _iotaApi = new IotaAPI() + { + IotaClient = _iotaClient, + LocalPoW = _localPoW + }; + } + + [TestMethod] + public void ShouldSendTransfer() + { + Transfer[] transfers = + { + new Transfer(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2, 0, TEST_MESSAGE, TEST_TAG) + }; + + SendTransferResponse str = + _iotaApi.SendTransfer( + TEST_SEED1, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers, + null, null, false, false, null); + + Assert.IsNotNull(str.Results); + } + + [TestMethod] + public void ShouldHaveEqualOrderTrytes() + { + var localResponse = _localPoW.AttachToTangle( + TEST_SEED1, TEST_SEED1, + 11, TEST_TRYTES_VALUE_TRANSFER); + + var remoteResponse = _iotaClient.AttachToTangle( + TEST_SEED1, TEST_SEED1, + 11, TEST_TRYTES_VALUE_TRANSFER); + + for (int i = 0; i < TEST_TRYTES_VALUE_TRANSFER.Length; i++) + { + Assert.AreEqual(new Transaction(localResponse.Trytes[i]).Value, + new Transaction(remoteResponse.Trytes[i]).Value); + } + } + + } +} diff --git a/Src/IotaSharp.Tests/Properties/AssemblyInfo.cs b/Src/IotaSharp.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8e6e3d3 --- /dev/null +++ b/Src/IotaSharp.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// [assembly: AssemblyTitle("IotaSharp.Tests")] +[assembly: AssemblyDescription("")] +// [assembly: AssemblyConfiguration("")] +// [assembly: AssemblyCompany("")] +// [assembly: AssemblyProduct("IotaSharp.Tests")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("a3668de7-3ec3-456e-966b-fb5880fb6783")] + +// [assembly: AssemblyVersion("1.0.*")] +// [assembly: AssemblyVersion("1.0.0.0")] +// [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Src/IotaSharp.Tests/TestUri.txt b/Src/IotaSharp.Tests/TestUri.txt new file mode 100644 index 0000000..bcb9f75 --- /dev/null +++ b/Src/IotaSharp.Tests/TestUri.txt @@ -0,0 +1,6 @@ +https://nodes.devnet.thetangle.org:443 +https://nodes.devnet.iota.org:443 + +https://node02.iotatoken.nl:443 + +http://node05.iotatoken.nl:16265 \ No newline at end of file diff --git a/IotaCSharpApiUnitTests/Api/Utils/ChecksumTest.cs b/Src/IotaSharp.Tests/Utils/ChecksumTest.cs similarity index 71% rename from IotaCSharpApiUnitTests/Api/Utils/ChecksumTest.cs rename to Src/IotaSharp.Tests/Utils/ChecksumTest.cs index ef962b5..13ef926 100644 --- a/IotaCSharpApiUnitTests/Api/Utils/ChecksumTest.cs +++ b/Src/IotaSharp.Tests/Utils/ChecksumTest.cs @@ -1,9 +1,11 @@ -using Iota.Lib.CSharp.Api.Utils; +using System.Diagnostics.CodeAnalysis; +using IotaSharp.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Iota.Lib.CSharpTests.Api.Utils +namespace IotaSharp.Tests.Utils { [TestClass] + [SuppressMessage("ReSharper", "StringLiteralTypo")] public class ChecksumTest { private static readonly string TEST_ADDRESS_WITHOUT_CHECKSUM = @@ -15,7 +17,7 @@ public class ChecksumTest [TestMethod] public void ShouldAddChecksum() { - Assert.AreEqual(Checksum.AddChecksum(TEST_ADDRESS_WITHOUT_CHECKSUM), TEST_ADDRESS_WITH_CHECKSUM); + Assert.AreEqual(TEST_ADDRESS_WITHOUT_CHECKSUM.AddChecksum(), TEST_ADDRESS_WITH_CHECKSUM); } [TestMethod] @@ -27,7 +29,7 @@ public void ShouldRemoveChecksum() [TestMethod] public void ShouldIsValidChecksum() { - Assert.AreEqual(TEST_ADDRESS_WITH_CHECKSUM.IsValidChecksum(), true); + Assert.IsTrue(TEST_ADDRESS_WITH_CHECKSUM.IsValidChecksum()); } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp.Tests/Utils/InputValidatorTest.cs b/Src/IotaSharp.Tests/Utils/InputValidatorTest.cs new file mode 100644 index 0000000..1086976 --- /dev/null +++ b/Src/IotaSharp.Tests/Utils/InputValidatorTest.cs @@ -0,0 +1,355 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using IotaSharp.Model; +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.Tests.Utils +{ + [TestClass] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class InputValidatorTest + { + // ReSharper disable StringLiteralTypo + private static readonly string TEST_ADDRESS_WITH_CHECKSUM = + "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA"; + + private static readonly string TEST_TRYTES = + "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"; + + private static readonly string TEST_HASH = + "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999"; + + private static readonly string TEST_MESSAGE = "IOTASHARP"; + + private static readonly string TEST_TAG = "IOTASHARPSPAM99999999999999"; + // ReSharper restore StringLiteralTypo + + [TestMethod] + public void ShouldIsTrits() + { + sbyte[] trits = + { + 1, 1, -1, -1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, + 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, + -1, -1, 1, 1, 0, 0, 1, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, + 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, + 0, 1, -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, + -1, 1, 1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, + 1, 0, 0, 0, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, 0, 1, 0, 1, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, 1, 1, 0, + 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, 1, 1, 0, + 1, 1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, + 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, -1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, 0, 1, 1, + 0, 0, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, 1, + -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, + 0, -1, 1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, 0, 1, 1, 1, 0, 0, + -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, -1, 1, + 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, + 0, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, -1, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, 0, -1, + 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, -1, 1, 1, + 0, 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, 0, -1, 0, 1, 0, + 0, 1, 0, 1, 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, -1, 0, 1, + 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, + 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 1, + 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, -1, 0, -1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 1, -1, + 0, 1, 0, 0, 0, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, -1, 1, 1, 1, + 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, + -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, + -1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, -1, -1, + 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, 0, -1, 1, 0, 0, + -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, + -1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, + 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, -1, 1, 0, 0, + -1, -1, 1, 1, 0, 0, 0, -1, -1, -1, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, + 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, + 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, + -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, + 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, + -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, + -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, + -1, 0, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, 1, -1, 1, 0, 0, -1, 1, 1, 1, 0, 1, -1, -1, 0, + 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, 1, 1, -1, 0, 1, 0, + -1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, 0, 0, + 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, + -1, -1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 1, 0, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, + -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, + 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, -1, 1, 0, 0, -1, + -1, 1, 1, 0, 0, -1, 0, 0, -1, 1, 0, 0, -1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, 1, + 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 1, + 1, 0, 0, -1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, + 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 0, -1, -1, 1, 0, -1, 1, 0, + 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, + 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, -1, + 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, + 0, 1, -1, -1, 0, 1, 0, 1, 1, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, + -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, -1, + 0, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, -1, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 0, + 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, + 0, 0, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 1, -1, 0, + 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, + -1, -1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, 0, + 0, -1, 1, 0, -1, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 1, + 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, + 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, + 1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, -1, 0, 1, 0, 1, -1, -1, 0, 1, 0, -1, 0, + -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, -1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, + 0, 1, -1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, + 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 1, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, + 0, 1, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 0, -1, 0, + 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, 1, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, + -1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, -1, + 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 0, -1, -1, 1, 0, -1, 1, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 0, 1, 1, 1, + 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, 1, 0, -1, 0, 1, 0, 0, 0, + 1, 1, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, + 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, + 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 0, -1, + 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, + 0, 1, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, + 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, + 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, -1, 0, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, + 0, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, + 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, + 0, -1, -1, 0, -1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, + -1, 1, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, -1, 1, + -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 0, 1, 1, + 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, 1, -1, 0, 1, 0, + -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 1, + 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, + 0, 0, -1, 0, 0, 1, 0, 0, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, 0, -1, 0, 1, 0, -1, + -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, 1, -1, + 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, -1, 0, -1, 1, 0, 0, + -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, + 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, + 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, + 0, 1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, -1, + -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, + -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, + 1, 0, 1, 1, 0, 0, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, + 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, -1, 1, 0, 0, 0, 0, 1, 1, + 0, 1, -1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, 0, 1, 0, 1, + 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, + 1, 0, 0, -1, 0, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, + 0, -1, 0, 0, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, -1, 0, + 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, + 1, -1, -1, 0, 1, 0, 1, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, + 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, + 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, + 0, -1, -1, 1, 1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, -1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 0, + -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, 0, 1, 1, 0, 0, 1, 0, 1, + 1, 0, 1, -1, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 0, -1, 1, 0, 0, + -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, + 0, 0, 1, 0, -1, 1, 0, 1, 1, 0, 0, 0, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, -1, 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 1, 0, + 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 0, 1, 0, 1, + 1, 0, 1, -1, 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, -1, -1, -1, 1, 0, -1, 0, 1, 1, 1, 0, + 0, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, -1, + -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, -1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, -1, 0, + 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, + -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, -1, 0, 1, 0, 1, -1, -1, 0, 1, 0, -1, -1, 1, + 1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, + -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, -1, 0, 1, 0, -1, + -1, 1, 1, 0, 0, 1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, -1, 0, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, -1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, + 1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 1, -1, + -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, + 0, -1, -1, 1, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, 1, 0, + -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, 0, 0, -1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, + 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, + -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, + 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 0, -1, 0, 0, 1, 0, -1, 0, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, -1, 0, + -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, -1, 0, + 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, + -1, 1, -1, 0, 1, 0, 1, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, + 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 1, 0, -1, 0, 1, 0, -1, 0, -1, 1, 0, + 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, 1, + 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, + 0, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, + 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, 1, 0, -1, 1, 0, + 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, -1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, 0, 1, 1, 0, 0, 0, -1, 0, 1, 0, -1, + -1, 1, 1, 0, 0, 1, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, + 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, + 0, -1, 1, 1, 1, 0, -1, 1, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, -1, + 1, 1, 0, 0, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, -1, 1, + 0, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, + 0, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, + -1, 0, 1, 0, 1, -1, -1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, -1, 0, 1, 0, 0, 0, 1, 1, 1, + 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, + -1, 1, 1, 0, 0, 0, -1, 0, -1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, + 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, 0, -1, 0, 1, 0, + 0, -1, 0, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, -1, + -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, + 0, -1, 1, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, + 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, -1, 0, 0, + 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, 0, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, + 0, -1, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, + 0, -1, 1, -1, 0, 1, 0, 0, 1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, -1, 0, + -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, + 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, 0, 1, 1, 1, 1, 0, -1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, + -1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, -1, 0, 1, + 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, -1, 1, + 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, -1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 1, 1, 1, + 0, 0, -1, 0, 0, 1, 0, 1, 0, -1, 0, 1, 0, 1, -1, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, 0, 1, 1, 0, 0, + -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, -1, 0, 1, 0, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 1, + 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, 0, 0, + 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 0, -1, -1, 1, 0, -1, 1, 0, 1, 1, 0, -1, -1, 1, 1, 0, + 0, 1, -1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + -1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, + 0, 1, 0, 0, -1, 1, 1, 1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, + -1, 0, 0, -1, 1, 0, -1, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 1, -1, 0, 1, + 0, 1, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, + -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, 0, 1, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, + -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, -1, -1, + -1, 1, 0, 0, 1, 0, 1, 1, 0, -1, 1, 0, 1, 1, 0, -1, 1, -1, 0, 1, 0, 0, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, + 0, -1, 1, -1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, -1, 0, 1, 0, -1, -1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, + 0, 0, 1, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 0, + -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, 1, -1, -1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 0, -1, 0, + 1, 0, 0, 0, 0, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 0, 1, 0, -1, 0, 1, 0, 0, -1, 0, 0, 1, 0, -1, + 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, -1, 0, 0, 1, 0, -1, -1, 1, + 1, 1, 0, 0, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, 1, 0, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, + 1, 1, 1, -1, 1, 0, 0, -1, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, 1, -1, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, -1, + -1, 0, 1, 0, -1, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1, 0, -1, 1, 0, 1, 1, + 0, 1, -1, 1, 1, 1, 0, -1, 1, -1, 0, 1, 0, -1, -1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, -1, -1, 0, 1, 0, -1, + 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, -1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, -1, 1, 1, -1, 1, -1, -1, 1, + 0, 0, 0, 1, -1, 1, 0, -1, 1, 0, 0, 0, -1, 0, 1, 0, 1, 0, -1, -1, -1, -1, 1, -1, 0, 1, 0, -1, 1, 1, 0, + -1, 0, -1, 1, -1, 0, 1, -1, -1, 1, 1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 0, 1, -1, 0, 1, 0, 1, -1, -1, 1, 0, + 1, 0, 1, 1, 1, -1, 1, 0, 0, 0, -1, 1, 0, 1, 1, -1, 1, 0, 0, 1, 0, 1, 0, -1, -1, -1, 0, -1, -1, 0, 0, -1, + -1, 1, 0, 0, -1, 0, -1, -1, 0, 0, -1, 1, 0, 0, 1, 0, 1, 0, 0, 1, -1, 1, -1, 0, 1, 0, -1, 0, 0, 0, -1, + -1, 1, 0, 0, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 0, 1, -1, 0, 0, -1, 0, 1, 1, 0, 1, 1, -1, + 1, 0, 1, 1, 0, -1, 0, 1, -1, 0, 1, 0, 1, -1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, -1, 0, 1, 1, 1, -1, 1, 0, -1, + 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 0, 0, -1, 0, 1, 1, 0, 1, -1, 0, 1, 0, -1, 1, -1, 1, 1, -1, 0, -1, 0, + 0, 0, 0, 0, -1, 0, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, + 1, 1, 1, -1, -1, 1, 1, 0, -1, 0, -1, -1, 1, 1, 1, -1, -1, 1, 0, 0, 0, -1, 1, -1, 1, 0, 0, 1, -1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, -1, 1, 0, -1, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 1, -1, 1, 0, + 0, 1, 1, 0, 1, 1, -1, 0, 1, 1, 0, 1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 1, 1, 0, -1, -1, 1, 0, 1, 0, 1, 1, 0, + 1, 1, 0, -1, 0, -1, 1, -1, 0, -1, -1, 0, -1, 1, 0, 0, -1, -1, -1, 0, 0, 1, -1, 0, -1, 1, 1, -1, 1, -1, + 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 1, -1, 0, 0, 1, -1, 1, 1, -1, 0, -1, -1, 1, 0, -1, 1, -1, 0, 1, 0, 1, 1, + -1, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 1, -1, 1, -1, 1, 1, 1, 0, 0, 1, 1, 0, -1, 0, 0, 1, 1, 1, -1, 0, 0, + -1, 0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 1, -1, 1, 0, -1, -1, 1, 1, 1, 0, -1, 0, 0, 1, 0, 1, -1, 0, 1, 1, + 1, 1, 0, 0, 0, 1, 1, -1, 1, 0, -1, 0, 1, -1, 1, -1, 0, 1, -1, -1, 0, -1, -1, 0, -1, 0, -1, 1, 1, -1, 0, + 0, 0, 1, -1, 1, 1, -1, -1, -1, 0, 1, 0, 1, 1, 0, -1, -1, 1, 0, -1, 1, 0, 0, 1, 0, 1, -1, -1, -1, -1, -1, + 0, 0, 0, 1, 0, -1, 1, 0, 0, -1, -1, -1, 0, 1, -1, -1, 1, -1, 1, 0, 0, 0, -1, 1, -1, 0, 0, 1, -1, 1, 1, + 0, -1, 1, 1, -1, 1, 0, -1, 1, 0, -1, 1, 1, 0, 0, 1, 1, -1, 1, -1, -1, 0, -1, 1, -1, -1, 1, -1, 0, -1, + -1, 0, 0, 1, 0, -1, 0, -1, -1, 0, 1, -1, 0, -1, 1, 1, 0, 0, 1, 1, 1, 0, -1, -1, -1, -1, -1, 0, 0, 0, 1, + 1, 1, -1, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, 1, 1, 0, -1, 1, 0, -1, 0, 0, -1, 1, 0, 1, -1, 1, 1, 0, -1, + -1, 1, 0, 1, -1, -1, 0, 1, 0, -1, 0, 0, 0, 0, 0, 1, 1, 1, 0, -1, -1, 0, -1, 0, -1, -1, 1, -1, -1, 1, 0, + -1, -1, 0, -1, 0, 1, 0, 0, -1, 0, 0, -1, 1, 0, 1, 1, -1, 0, -1, 1, 1, 0, -1, 0, 0, 1, 1, 0, -1, -1, -1, + 0, -1, 0, 1, 0, 1, 0, 1, -1, 1, 0, 0, -1, -1, -1, 1, 0, 0, 0, 0, 0, 1, -1, -1, 1, -1, -1, 0, 0, 1, 0, 0, + 1, 1, 0, -1, -1, 0, 0, 1, 1, -1, 0, 1, -1, 0, -1, 0, 1, 1, 0, 1, 1, 0, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, -1, 0, 1, -1, 1, 0, 0, 1, 1, 1, 1, 1, 0, -1, 0, 1, 1, 0, -1, 1, 1, -1, 1, -1, + -1, 1, -1, 1, 1, -1, -1, 0, 1, 0, 1, -1, 0, -1, -1, -1, 1, 1, -1, 1, -1, 0, 0, 1, 1, 1, 1, -1, 1, 0, 1, + -1, -1, -1, -1, 1, 0, 1, -1, 0, 1, 0, 1, 1, 0, -1, -1, 1, 0, 1, -1, 1, 0, -1, 1, 0, 0, 0, 0, -1, -1, -1, + 0, 0, 0, 0, -1, 1, 0, -1, -1, 0, 0, -1, -1, -1, -1, 1, -1, 1, 0, 1, 1, 0, -1, -1, -1, 0, 0, 0, -1, 0, + -1, 0, 1, 1, -1, -1, 1, 0, 0, -1, -1, 1, -1, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, -1, -1, -1, 1, 0, 1, 0, 0, + 0, 0, 1, 0, 1, -1, -1, 0, 0, 1, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1, 0, 1, -1, 0, -1, -1, 1, -1, -1, 0, -1, 0, + 1, 0, 1, -1, 0, 0, -1, 1, -1, 1, 0, 0, 0, -1, -1, 1, 0, 1, 1, 1, 1, -1, 1, -1, 0, 0, -1, 1, -1, -1, 1, + -1, 1, 1, -1, -1, 1, -1, 0, 0, 1, -1, -1, -1, 0, -1, 1, -1, -1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, -1, -1, 0, -1, -1, 1, 1, 0, -1, 0, -1, -1, 1, 1, 1, -1, -1, 1, 0, 0, 0, -1, 1, -1, 1, 0, 0, 1, + -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1, 0, -1, 1, 0, + -1, 1, 1, 0, 1, 1, 1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 0, + -1, 0, 1, 0, -1, 0, 1, -1, 1, 1, 0, 1, -1, 1, -1, -1, 1, -1, -1, 1, 0, -1, 1, -1, 1, -1, -1, 0, 1, 0, 0, + -1, 1, -1, 0, 1, 0, 1, -1, -1, 1, 1, 0, 1, 0, -1, 0, -1, 1, 0, 0, -1, 1, 1, 0, 1, -1, 1, -1, 0, 1, 1, + -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 0, 1, -1 + }; + + Assert.IsTrue(InputValidator.IsTrits(trits)); + } + + [TestMethod] + public void ShouldIsAddress() + { + Assert.IsTrue(InputValidator.IsAddress(TEST_ADDRESS_WITH_CHECKSUM)); + } + + [TestMethod] + public void ShouldCheckAddress() + { + Assert.IsTrue(InputValidator.CheckAddress(TEST_ADDRESS_WITH_CHECKSUM)); + } + + [TestMethod] + public void ShouldIsTrytes() + { + Assert.IsTrue(InputValidator.IsTrytes(TEST_TRYTES, TEST_TRYTES.Length)); + } + + [TestMethod] + public void ShouldIsValue() + { + Assert.IsTrue(InputValidator.IsValue("1234")); + } + + [TestMethod] + public void ShouldIsArrayOfHashes() + { + Assert.IsTrue(InputValidator.IsArrayOfHashes(new[] {TEST_HASH, TEST_HASH})); + } + + [TestMethod] + public void ShouldIsArrayOfTrytes() + { + Assert.IsTrue(InputValidator.IsArrayOfTrytes(new[] {TEST_TRYTES, TEST_TRYTES})); + } + + [TestMethod] + public void ShouldInvalidTxTrytes() + { + // We fail on having value above max supply + Assert.IsFalse(InputValidator.IsArrayOfRawTransactionTrytes(new[] {TEST_TRYTES, TEST_TRYTES})); + } + + [TestMethod] + public void ShouldIsNinesTrytes() + { + Assert.IsTrue(InputValidator.IsNinesTrytes("999999999", 9)); + } + + [TestMethod] + public void ShouldIsValidTransfer() + { + var transfer = new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG); + Assert.IsTrue(InputValidator.IsValidTransfer(transfer)); + } + + [TestMethod] + public void ShouldIsTransfersCollectionValid() + { + List transfers = new List + { + new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG), + new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0), + new Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0) + }; + + Assert.IsTrue(InputValidator.IsValidTransfersCollection(transfers)); + } + } +} diff --git a/IotaApi.Standard.Tests/Utils/IotaUnitConverterTests.cs b/Src/IotaSharp.Tests/Utils/IotaUnitConverterTest.cs similarity index 61% rename from IotaApi.Standard.Tests/Utils/IotaUnitConverterTests.cs rename to Src/IotaSharp.Tests/Utils/IotaUnitConverterTest.cs index 7ce33a3..52016d2 100644 --- a/IotaApi.Standard.Tests/Utils/IotaUnitConverterTests.cs +++ b/Src/IotaSharp.Tests/Utils/IotaUnitConverterTest.cs @@ -1,7 +1,7 @@ -using Iota.Api.Standard.Utils; +using IotaSharp.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Iota.Api.Standard.Tests.Utils +namespace IotaSharp.Tests.Utils { [TestClass] public class IotaUnitConverterTest @@ -9,53 +9,53 @@ public class IotaUnitConverterTest [TestMethod] public void ShouldConvertUnitItoKi() { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Iota, IotaUnits.Kilo), 1); + Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.IOTA, IotaUnits.KiloIOTA), 1); } [TestMethod] public void ShouldConvertUnitKiToMi() { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Kilo, IotaUnits.Mega), 1); + Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.KiloIOTA, IotaUnits.MegaIOTA), 1); } [TestMethod] public void ShouldConvertUnitMiToGi() { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Mega, IotaUnits.Giga), 1); + Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.MegaIOTA, IotaUnits.GigaIOTA), 1); } [TestMethod] public void ShouldConvertUnitGiToTi() { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Giga, IotaUnits.Terra), 1); + Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.GigaIOTA, IotaUnits.TerraIOTA), 1); } [TestMethod] public void ShouldConvertUnitTiToPi() { - Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.Terra, IotaUnits.Peta), 1); + Assert.AreEqual(IotaUnitConverter.ConvertUnits(1000, IotaUnits.TerraIOTA, IotaUnits.PetaIOTA), 1); } [TestMethod] public void ShouldFindOptimizeUnitToDisplay() { - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1), IotaUnits.Iota); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000), IotaUnits.Kilo); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000), IotaUnits.Mega); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000), IotaUnits.Giga); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000000L), IotaUnits.Terra); - Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000000000L), IotaUnits.Peta); + Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1), IotaUnits.IOTA); + Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000), IotaUnits.KiloIOTA); + Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000), IotaUnits.MegaIOTA); + Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000), IotaUnits.GigaIOTA); + Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000000L), IotaUnits.TerraIOTA); + Assert.AreEqual(IotaUnitConverter.FindOptimalIotaUnitToDisplay(1000000000000000L), IotaUnits.PetaIOTA); } - /*[TestMethod] - public void shouldConvertRawIotaAmountToDisplayText() + [TestMethod] + public void ShouldConvertRawIotaAmountToDisplayText() { - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1, false), "1 i"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000, false), "1 Ki"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000, false), "1 Mi"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000, false), "1 Gi"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000L, false), "1 Ti"); - Assert.AreEqual(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000000L, false), "1 Pi"); - }*/ + Assert.AreEqual(IotaUnitConverter.ConvertRawIotaAmountToDisplayText(1, false), "1 i"); + Assert.AreEqual(IotaUnitConverter.ConvertRawIotaAmountToDisplayText(1000, false), "1 Ki"); + Assert.AreEqual(IotaUnitConverter.ConvertRawIotaAmountToDisplayText(1000000, false), "1 Mi"); + Assert.AreEqual(IotaUnitConverter.ConvertRawIotaAmountToDisplayText(1000000000, false), "1 Gi"); + Assert.AreEqual(IotaUnitConverter.ConvertRawIotaAmountToDisplayText(1000000000000L, false), "1 Ti"); + Assert.AreEqual(IotaUnitConverter.ConvertRawIotaAmountToDisplayText(1000000000000000L, false), "1 Pi"); + } } -} \ No newline at end of file +} diff --git a/IotaCSharpApiUnitTests/Api/Utils/MultisigTest.cs b/Src/IotaSharp.Tests/Utils/MultisigTest.cs similarity index 78% rename from IotaCSharpApiUnitTests/Api/Utils/MultisigTest.cs rename to Src/IotaSharp.Tests/Utils/MultisigTest.cs index e42bb69..6bbd17b 100644 --- a/IotaCSharpApiUnitTests/Api/Utils/MultisigTest.cs +++ b/Src/IotaSharp.Tests/Utils/MultisigTest.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; -using Iota.Lib.CSharp.Api; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Pow; -using Iota.Lib.CSharp.Api.Utils; +using IotaSharp.Model; +using IotaSharp.Pow; +using IotaSharp.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Iota.Lib.CSharpTests.Api.Utils +namespace IotaSharp.Tests.Utils { [TestClass] public class MultisigTest @@ -23,12 +22,15 @@ public class MultisigTest private const string TestTag = "JOTASPAM9999999999999999999"; - private IotaApi _iotaClient; + private IotaAPI _iotaAPI; [TestInitialize] public void CreateApiClientInstance() { - _iotaClient = new IotaApi("node.iotawallet.info", 14265); + _iotaAPI = new IotaAPI() + { + IotaClient = new IotaClient("https://nodes.devnet.iota.org:443") + }; } [TestMethod] @@ -40,13 +42,13 @@ public void BasicMultiSigTest() string digestOne = ms.GetDigest(TestSeed1, 3, 0); // We initiate the multisig address generation by absorbing the key digest - ms.AddAddressDigest(new[] {digestOne}); + ms.AddAddressDigest(new[] { digestOne }); // Second cosigner also uses security level 3 and index 0 for the private key string digestTwo = ms.GetDigest(TestSeed2, 3, 0); // Add the multisig by absorbing the second cosigners key digest - ms.AddAddressDigest(new[] {digestTwo}); + ms.AddAddressDigest(new[] { digestTwo }); // finally we generate the multisig address itself string multiSigAddress = ms.FinalizeAddress(); @@ -55,7 +57,7 @@ public void BasicMultiSigTest() bool isValidMultisigAddress = ms.ValidateAddress(multiSigAddress, - new[] {Converter.ToTrits(digestOne), Converter.ToTrits(digestTwo)}); + new[] { Converter.ToTrits(digestOne), Converter.ToTrits(digestTwo) }); Console.WriteLine("Is a valid multisig address " + isValidMultisigAddress); @@ -63,11 +65,11 @@ public void BasicMultiSigTest() List transfers = new List { - new Transfer(ReceiveAddress, 999, "", TestTag) + new Transfer(ReceiveAddress.AddChecksum(), 999, "", TestTag) }; List trxs = - _iotaClient.InitiateTransfer(6, multiSigAddress, RemainderAddress, transfers, true); + _iotaAPI.InitiateTransfer(6, multiSigAddress.AddChecksum(), RemainderAddress, transfers, null, true); Bundle bundle = new Bundle(trxs, trxs.Count); @@ -81,7 +83,7 @@ public void BasicMultiSigTest() bool isValidSignature = sgn.ValidateSignatures(bundle, multiSigAddress); Console.WriteLine("Result of multi-signature validation is " + isValidSignature); Assert.IsTrue(isValidSignature, "MultiSignature not valid"); - + } } } diff --git a/Src/IotaSharp.Tests/Utils/SeedRandomGeneratorTest.cs b/Src/IotaSharp.Tests/Utils/SeedRandomGeneratorTest.cs new file mode 100644 index 0000000..7c12ef6 --- /dev/null +++ b/Src/IotaSharp.Tests/Utils/SeedRandomGeneratorTest.cs @@ -0,0 +1,17 @@ +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.Tests.Utils +{ + [TestClass] + public class SeedRandomGeneratorTest + { + [TestMethod] + public void ShouldGenerateNewSeed() + { + string generatedSeed = SeedRandomGenerator.GenerateNewSeed(); + Assert.IsTrue(InputValidator.IsValidSeed(generatedSeed)); + Assert.AreEqual(generatedSeed.Length, Constants.SEED_LENGTH_MAX); + } + } +} diff --git a/IotaApi.Standard.Tests/Utils/SigningTest.cs b/Src/IotaSharp.Tests/Utils/SigningTest.cs similarity index 70% rename from IotaApi.Standard.Tests/Utils/SigningTest.cs rename to Src/IotaSharp.Tests/Utils/SigningTest.cs index 0e5a5e8..358babd 100644 --- a/IotaApi.Standard.Tests/Utils/SigningTest.cs +++ b/Src/IotaSharp.Tests/Utils/SigningTest.cs @@ -1,48 +1,51 @@ -using System; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Pow; -using Iota.Api.Standard.Utils; +using System.Diagnostics.CodeAnalysis; +using IotaSharp.Model; +using IotaSharp.Pow; +using IotaSharp.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Iota.Api.Standard.Tests.Utils +namespace IotaSharp.Tests.Utils { [TestClass] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "IdentifierTypo")] public class SigningTest { - private static readonly string TEST_SEED = + private const string TEST_SEED = "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA"; - private static readonly string FIRST_ADDR = + private const string FIRST_ADDR = "LXQHWNY9CQOHPNMKFJFIJHGEPAENAOVFRDIBF99PPHDTWJDCGHLYETXT9NPUVSNKT9XDTDYNJKJCPQMZCCOZVXMTXC"; - private static readonly string SIXTH_ADDR = + private const string SIXTH_ADDR = "HLHRSJNPUUGRYOVYPSTEQJKETXNXDIWQURLTYDBJADGIYZCFXZTTFSOCECPPPPY9BYWPODZOCWJKXEWXDPUYEOTFQA"; - private static readonly string SIGNATURE1 = + private const string SIGNATURE1 = "PYWFM9MYTPNZ9HTLZBBB9CGQWKPALDUNAQYCAA9VMQ9UMBLLAXSPPHQSNAAKJA9MZBXBHBQBFFKMBSDHDTCVCDWLUYCEQ9YZJAJAXXXZHDWTSLWGIWRE9LJFVWAFUMOAGHDBHJQ9APNBLSX9GPTJNTO9SBJT9UKYCZXYAWVGXEBJANNWEWZSPRYHASHGIFUWOEHUFMP9MWQBYZOZESCPLVJUCWGLEJIDPMEVNPBITBNFSQ9GBWCDTQZOPLPXOWWNQAEIXQRWMHAQDH9C9KKHGNKAX9INMUVVGIK9TPGRHOMDFAB9VICYDMSHHDDBRSTEFSZXMXFJUQRRAFBSCNHSMKRNNTTCMBURKBGC9EDWKLPBSQAKYCUKKSZWRVURZGUA9QVSXXPICIYFHLPJSWEFBZPUTWWNIKSAJM9OMRFFQVFJZZHLQBSEYXM9CN9HCGHSJBTYDGWOQPXOPZZE9EPQAQFT9GDWZCSOPMZHYYZXDDZ9DJDLOOOTIFQANFANNAYVIRUNDXSB9XRNXJYRDBLTEDWSUOVISMCHGKD9KDRSFDWRSVZQQKGAMDXFAWBSLMTTUMH9RAUIVI9HJMTODACSOP9MLHOJMSIWQ9TTNGPXRNWRHLMEMAH9GZHJRNJHQNBBLWKFXIZBMGMATZIZBFDPAFDCLDIFFAIK9JUSFYYC9ANDGXCZFLZYGURTUI9SWYYRGDJAHXDDNHSJZBCENZUSQXSFZMTXSFLRK9RIYAUMHPBOBNOXCHDIMBGIBVOOHIDQ9ORHHDECDTREIEILWDUFMUWYMGIXBIKRZMKGXTYZTX9GKFP9AUXMTUUQXRHHKPYULGJFJLEEYCNKLOWULRIAFM9OYKEDFRXFVTSJMSEMOURCLNOIETIHEUCMPLWKDXDO9TAHVH99MKTBAAKCMYKLJUQIVLLSVTFUM9KDSIHYXYHPRLDADSLSSOIGLLXMPKTHS9YXUNMUTBTBPDWXA9GVTBGLTCLEZEUNNIRBBURDWOFFYXELPFSZRQARVRPHGETKJTRUZIFDDWBOHHGUZTODZFMOVMAGCYCTGBWSGAVZADIPIASCKTRKIUUMHNGUYZKDVOPKKHXD9EXVUVJ9YFNYMLIJLEEGPIZLFS9FIEMG9MIEO9FPW9JZEVDQOECMTESICSMVWXZNXXJILJLVQHEBHQWPOBHKEGRLFCPLB9ZECJOZDAB9DMU9UALBIQDABVDYRRTPMZOCQX9WNGXVNKQZWPA9ACVONQMRHQDPPIQTP9VKP9PAORNOFTZZWGC9RYBWSNLULZGYLMYIWWPDMOHPZTQWRPRCN9RAUOKDSCWBRI9NPUPLBILOZDOOPHSWQGJEGUYWAWJDEBLEOBSYYU9XSRPBHRUQXIDOWJZQQVJTMP9VLWLOGBK9FZFHYLJCNENDATNPSF99DFPVPTNNKIUMHRGEBJXNUVENAHYLFPPHYFTIKCB9DBVCCSJTDMOMISBAAEJVBVLHOADKNFG9NQGIGRDICQCWZVHGGXLTUNQKBUTLDWXIM9REWBLIXFBPTOXBLWBQQUSRLRDHTXQWARPMBQILAJSYLLTDAGTFPCXBCDITDOIZNGKPZQWWHJDZIPYCPFEYFD9CVXYOJHJNUNMCMSIAUVSKCACNNPGDYJJVTZOREJOPIBYCMBULMTSDTJPZNVNYQBQPPABOSSNZJKQQZ9LULSHJUBLHIFMYWSNPGUERCLVFV9LOEBJEERYHI9OMSMSCDFDLNHEMLQXNRJDYSNKTOYCPTAUWAWIGCPJKMAMGLXNBJMO9BZGFIHWDVJWYCNZZV9KBWIFQSMAXBPGVXDW9SLTHOLMJORRXZJSTNOQDRGNBLGTFCCNBJECYZGWTDRJKJRBAJRCULMOUBQJFWCLWMEWGAAVNZWMDWBYDKZMUCZAKXQLRQPIQJPMORKJXKSDTGXWDHAKUOSMXCFXWSZYWXODWFACBMFSWQFVMBELPZMISVWRQQQPNHOTWOEQQAQJDLXFEEBXLJQEECWG9ARRRDLTVBHTPARJMLOZHYWDCSXPTZCNZWTCRUJNZWKFZXAARPHFCBTLWSLERGJJMKIG9NEBADRMZWYNWIRGTMOBRKURUE9GDLRIEODY9BXJOZUVNCXKXFPFDXKUTMXZRJDOQ9YTV9BJDKGZBYTWGVPQQMNVCNARLPSRQWN9TRMHWLNEJZFTCSRD"; - private static readonly string SIGNATURE2 = + private const string SIGNATURE2 = "URKFKLNXFEKDOGSQVMAOPEDIWSMTCKJZ9KEVWYALY9JAO9KHUGNDTMGQLKQJUIPWDIVMPEDSVPLFMDCIXDDT9WBBRTFQENL9AXLSBYHINXCDYBFGRNKJDYHAQVJKWCVOYXHTNBEZUNLVMJLUMZYJFAOW9PVVMJZNZZFJQEQFELVFZVFVWPJ9WQZJLPSGBYECHXSFVFQJGUCPFXC9GATTILVCAANNHOYMLOYX9QSUPCERYCOXPACZEEGLREBRZWXGUTTVTHB9GBRCIFEOBPIRXXPQKRSODEHDSZXLGIKXUQWNTQKIOPVDVSIK9WJUAEFOJBU9MBPBSVYSCLBMINTT9ZCTREZSMSVOPXSZOMCGFEZKMOCNLJ9QUTAPKBHRIAIYLCHUQHOINKSCMXWZVDGDXHNJQXJHPCCGBEWROVKEPAPBFFRCAVXZWIRKCRAWYHIHMDXFAGDJQNJJPYSQUHKFOOCEVQOGRQEIOQFKZWUQ9XVRNXKGMJOQEZHQZXQABWUQRBKXWHYUXEAEMDGXVY9WS9VJOCMGBQASSRNKAYJPTSPQEMYSJMTCLMDQJKDPBGQZZSFBDOKHBYY9UDRXNKTPWBCQTVKUGMEDUXL9TTKPATNIKVAGHACHPFSCRYNIRJBQC9OADPGWBFYYARSVNQCGMYQGCYLZH9KLMUIJPCLPQVS9BORXCJBXPDECJGKDNOUYWTKKFLXZARWKGUSMVMXKJTMRYZRERFCFGTZFZFCAOQSZGPQJUEZUJLJPU9QPMJUTZNLMSMPRGIFHUUZHMPMRBEBATEIIWPCOIMWOYOG9NYFBYOWFDKRXOTREBU99GNCPXKOWGI99LNVPRFFF9FCLFXI9HMUFU9NRLNJVTFNUSUJTAVOG9GKUYYEXIM9HTPIDTWIGLKRAQPKMQVZAPYMPSQIOJ9JZBWDMQHDSSRSHNCWSAJCSRORSEXLLQNZUKPXPGRLYMXOXWCCWWSBALFLXPHSGFLTOAFWPETBKJUMBLHMSKYLPJT9EJAZCPPNZWKPVCGKDJCRCLBBIAKVDSNWGONPLKFAYXZDI9FKPHDPKCB9UUPXLJVQTXOAZOQDRNSONXDVSLQGZYRIPGREYHRAUOSBFZDZPZHFNMWCZQGPXCZVLNCSASB9RQDFHOYMUVYLFKOEEWNREYCDMCTZIAFBFKLKRQWZCJHQZCZGWXIFTKRVMPHMVHAABHBDEV9WDEZBR9FLXLNBVNYKUOUFJQKNZVZVGZDDTFYNYFUVRLZKOLXXQYNV9MDVBLZSERXPGYKRIEZQZD9IBKFDT9AIYGWJJCXFWDUDURGJQLXVEJAVEOMZUVVTNCVBXEVQRDQIEHDUCSLCIJUTSCLFXEGMFYP9YLXELCZPMTBZWBIODZCFNJLVWTPQGLMQIHIABAYGJFFMOEDTCXGEDTNXMVXZYFGXRKVVRTIZ9ISXTDHAFPEKQZSM9XXQLOYBLTMD9MBERBIBEJDEXGMOLDZPZVVEPIRKJBDPAKFAWJPTCJSHZPDUKZEEHRFLMZCUGCOWFJBSTDGPHUIXSPPPHRQARMCFMTWKYPJNJQV9VSFZ9EWB9GVEAFUXHWRNUXQLCSBWROOITBATWUXUYGSMGAXKGEBP9ZJWXQWHBVPOSLDHTWXUOFQNO9EXSYPQF9LQLQAFNRU9MTIIRQLBBBYKUPANWRQKGESFARQIRUTGFMZVUKHZJYKTYOARTDOBIYBFRHJWEFHCYVHRHTLTWBRMUDVIVQVNELQMQRXYDNGVSICZINWIZCIWVFXLYOLYKWDNWCWFZUXHUWOPRDHMTSXOZX9CVHANU9ZXTJOGKEPYR9CHGOTIUQSWIALAOIKHQFXWY9ZWTSZADVXJNNZOLSCXVVFBRHLRBTGMSZOYNIXTAMABKGJTLGTZKRHOPPJMNYIQNVKRGXUQDWYEIEZYM9CSXO9YLSBJLDJUWOLUXDEKBGGEIDEXFLZMESDOITNYTNRLGOMHJH9HOLXJABUNLXCZYTXFPZMHRJPLXSVPDBJBBZX9TBIMZZFZOXUSFEJYHEXPFXGJCQTBBLPEEWAPHUETGXSXYYAF9PCCCOONRMQGAPJ9JO9BZQ9QSKTPFFYIFVHSLAZY9CWYSIMKDOSLRKWBHPGJGVEJEEMLCCWXKSOCMBMZZZJWYBBXE9FTAYJALGWITJRXAXWZEXMECTZEEIWZPHYX"; - private static readonly string ADDR_SEED = + private const string ADDR_SEED = "LIESNFZLPFNWAPWXBLKEABZEEWUDCXKTRKZIRTPCKLKWOMJSEREWKMMMODUOFWM9ELEVXADTSQWMSNFVD"; - private static readonly string ADDR_I0_S1 = + private const string ADDR_I0_S1 = "HIPPOUPZFMHJUQBLBVWORCNJWAOSFLHDWF9IOFEYVHPTTAAF9NIBMRKBICAPHYCDKMEEOXOYHJBMONJ9D"; - private static readonly string ADDR_I0_S2 = + private const string ADDR_I0_S2 = "BPYZABTUMEIOARZTMCDNUDAPUOFCGKNGJWUGUXUKNNBVKQARCZIXFVBZAAMDAFRS9YOIXWOTEUNSXVOG9"; - private static readonly string ADDR_I0_S3 = + private const string ADDR_I0_S3 = "BYWHJJYSHSEGVZKKYTJTYILLEYBSIDLSPXDLDZSWQ9XTTRLOSCBCQ9TKXJYQAVASYCMUCWXZHJYRGDOBW"; - private static readonly string ADDR_LS_I0_S1 = + private const string ADDR_LS_I0_S1 = "VKPCVHWKSCYQNHULMPYDZTNKOQHZNPEGJVPEHPTDIUYUBFKFICDRLLSIULHCVHOHZRHJOHNASOFRWFWZC"; - private static readonly string ADDR_LS_I0_S2 = + private const string ADDR_LS_I0_S2 = "PTHVACKMXOKIERJOFSRPBWCNKVEXQ9CWUTIJGEUORSKWEDDJCBFQCCBQZLTYXQCXEDWLTMRQM9OQPUGNC"; - private static readonly string ADDR_LS_I0_S3 = + private const string ADDR_LS_I0_S3 = "AGSAAETPMSBCDOSNXFXIOBAE9MVEJCSWVP9PAULQ9VABOTWLDMXID9MXCCWQIWRTJBASWPIJDFUC9ISWD"; [TestMethod] @@ -63,18 +66,18 @@ public void TestAddressGeneration() [TestMethod] public void TestLongSeedKeyGeneration() { - ICurl curl = new Kerl(); - var signing = new Signing(curl); - var seed = "EV9QRJFJZVFNLYUFXWKXMCRRPNAZYQVEYB9VEPUHQNXJCWKZFVUCTQJFCUAMXAHMMIUQUJDG9UGGQBPIY"; + ICurl curl = SpongeFactory.Create(SpongeFactory.Mode.KERL); + Signing signing = new Signing(curl); + string seed = "EV9QRJFJZVFNLYUFXWKXMCRRPNAZYQVEYB9VEPUHQNXJCWKZFVUCTQJFCUAMXAHMMIUQUJDG9UGGQBPIY"; - for (var i = 1; i < 5; i++) + for (int i = Constants.MIN_SECURITY_LEVEL; i < Constants.MAX_SECURITY_LEVEL; i++) { - var key1 = signing.Key(Converter.ToTrits(seed), 0, i); - Assert.AreEqual(Signing.KeyLength * i, key1.Length); - var key2 = signing.Key(Converter.ToTrits(seed + seed), 0, i); - Assert.AreEqual(Signing.KeyLength * i, key2.Length); - var key3 = signing.Key(Converter.ToTrits(seed + seed + seed), 0, i); - Assert.AreEqual(Signing.KeyLength * i, key3.Length); + sbyte[] key1 = signing.Key(Converter.ToTrits(seed), 0, i); + Assert.AreEqual(Constants.KEY_LENGTH * i, key1.Length); + sbyte[] key2 = signing.Key(Converter.ToTrits(seed + seed), 0, i); + Assert.AreEqual(Constants.KEY_LENGTH * i, key2.Length); + sbyte[] key3 = signing.Key(Converter.ToTrits(seed + seed + seed), 0, i); + Assert.AreEqual(Constants.KEY_LENGTH * i, key3.Length); } } @@ -84,55 +87,44 @@ public void TestSigning() // we can sign any hash, so for convenience we will sign the first // address of our test seed // (but remove the checksum) with the key of our fifth address - var hashToSign = FIRST_ADDR.RemoveChecksum(); - var signing = new Signing(null); - var key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 2); - var normalizedHash = new Bundle().NormalizedBundle(hashToSign); - - var subKey = new int[6561]; - var subNormalizedHash = new int[27]; - - Array.Copy(key, 0, subKey, 0, 6561); - Array.Copy(normalizedHash, 0, subNormalizedHash, 0, 27); - var signature = signing.SignatureFragment( - subNormalizedHash, - subKey); + string hashToSign = FIRST_ADDR.RemoveChecksum(); + Signing signing = new Signing(); + sbyte[] key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 2); + sbyte[] normalizedHash = new Bundle().NormalizedBundle(hashToSign); + sbyte[] signature = signing.SignatureFragment( + ArrayUtils.SubArray(normalizedHash, 0, 27), + ArrayUtils.SubArray(key, 0, 6561)); Assert.AreEqual(SIGNATURE1, Converter.ToTrytes(signature)); - Array.Copy(key, 6561, subKey, 0, 6561); - Array.Copy(normalizedHash, 27, subNormalizedHash, 0, 27); - var signature2 = signing.SignatureFragment( - subNormalizedHash, - subKey); + sbyte[] signature2 = signing.SignatureFragment( + ArrayUtils.SubArray(normalizedHash, 27, 27 * 2), + ArrayUtils.SubArray(key, 6561, 6561 * 2)); Assert.AreEqual(SIGNATURE2, Converter.ToTrytes(signature2)); } [TestMethod] public void TestKeyLength() { - var signing = new Signing(null); - var key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 1); - Assert.AreEqual(Signing.KeyLength, key.Length); + Signing signing = new Signing(); + sbyte[] key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 1); + Assert.AreEqual(Constants.KEY_LENGTH, key.Length); key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 2); - Assert.AreEqual(2 * Signing.KeyLength, key.Length); + Assert.AreEqual(2 * Constants.KEY_LENGTH, key.Length); key = signing.Key(Converter.ToTrits(TEST_SEED), 5, 3); - Assert.AreEqual(3 * Signing.KeyLength, key.Length); + Assert.AreEqual(3 * Constants.KEY_LENGTH, key.Length); } + [TestMethod] public void TestVerifying() { - var signing = new Signing(null); - Assert.IsTrue(signing.ValidateSignatures( - RemoveChecksum(SIXTH_ADDR), - new[] {SIGNATURE1, SIGNATURE2}, - RemoveChecksum(FIRST_ADDR))); - } - - private string RemoveChecksum(string address) - { - Assert.IsTrue(address.IsValidChecksum()); - return address.Substring(0, 81); + Assert.IsTrue( + new Signing().ValidateSignatures( + SIXTH_ADDR.RemoveChecksum(), new[] + { + SIGNATURE1, SIGNATURE2 + }, FIRST_ADDR.RemoveChecksum())); } } -} \ No newline at end of file +} + diff --git a/Src/IotaSharp.Tests/Utils/TrytesConverterTest.cs b/Src/IotaSharp.Tests/Utils/TrytesConverterTest.cs new file mode 100644 index 0000000..2e52bb7 --- /dev/null +++ b/Src/IotaSharp.Tests/Utils/TrytesConverterTest.cs @@ -0,0 +1,45 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using IotaSharp.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IotaSharp.Tests.Utils +{ + [TestClass] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class TrytesConverterTest + { + private static readonly Random Random = new Random((int) DateTime.Now.Ticks); + + [TestMethod] + public void ShouldConvertStringToTrytes() + { + Assert.AreEqual(TrytesConverter.AsciiToTrytes("Z"), "IC"); + Assert.AreEqual(TrytesConverter.AsciiToTrytes("IOTA SHARP"), "SBYBCCKBEABCRBKBACZB"); + } + + [TestMethod] + public void ShouldConvertTrytesToString() + { + Assert.AreEqual(TrytesConverter.TrytesToAscii("IC"), "Z"); + Assert.AreEqual(TrytesConverter.TrytesToAscii("SBYBCCKBEABCRBKBACZB"), "IOTA SHARP"); + } + + [TestMethod] + public void ShouldConvertBackAndForth() + { + string str = RandomString(1000); + string back = TrytesConverter.TrytesToAscii(TrytesConverter.AsciiToTrytes(str)); + + Assert.AreEqual(str, back); + } + + private static string RandomString(int length) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[Random.Next(s.Length)]).ToArray()); + } + } +} diff --git a/Src/IotaSharp.sln b/Src/IotaSharp.sln new file mode 100644 index 0000000..e06672d --- /dev/null +++ b/Src/IotaSharp.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.705 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IotaSharp", "IotaSharp\IotaSharp.csproj", "{D7602B86-338C-4B56-ADA4-D941305799C1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IotaSharp.Tests", "IotaSharp.Tests\IotaSharp.Tests.csproj", "{A3668DE7-3EC3-456E-966B-FB5880FB6783}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IotaSharp.MAM", "IotaSharp.MAM\IotaSharp.MAM.csproj", "{E36AD688-47F7-4485-B26D-9D70CAC262DF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IotaSharp.MAM.Tests", "IotaSharp.MAM.Tests\IotaSharp.MAM.Tests.csproj", "{2371ADD6-AD27-4CE7-81A2-B44BD5BA45E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D7602B86-338C-4B56-ADA4-D941305799C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7602B86-338C-4B56-ADA4-D941305799C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7602B86-338C-4B56-ADA4-D941305799C1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7602B86-338C-4B56-ADA4-D941305799C1}.Release|Any CPU.Build.0 = Release|Any CPU + {A3668DE7-3EC3-456E-966B-FB5880FB6783}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3668DE7-3EC3-456E-966B-FB5880FB6783}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3668DE7-3EC3-456E-966B-FB5880FB6783}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3668DE7-3EC3-456E-966B-FB5880FB6783}.Release|Any CPU.Build.0 = Release|Any CPU + {E36AD688-47F7-4485-B26D-9D70CAC262DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E36AD688-47F7-4485-B26D-9D70CAC262DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E36AD688-47F7-4485-B26D-9D70CAC262DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E36AD688-47F7-4485-B26D-9D70CAC262DF}.Release|Any CPU.Build.0 = Release|Any CPU + {2371ADD6-AD27-4CE7-81A2-B44BD5BA45E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2371ADD6-AD27-4CE7-81A2-B44BD5BA45E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2371ADD6-AD27-4CE7-81A2-B44BD5BA45E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2371ADD6-AD27-4CE7-81A2-B44BD5BA45E2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FC425F28-8EF1-4F44-82F4-E4259B2323CE} + EndGlobalSection +EndGlobal diff --git a/Src/IotaSharp.sln.DotSettings b/Src/IotaSharp.sln.DotSettings new file mode 100644 index 0000000..e20df29 --- /dev/null +++ b/Src/IotaSharp.sln.DotSettings @@ -0,0 +1,20 @@ + + API + IOTA + POST + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/Src/IotaSharp/Core/AddNeighborsRequest.cs b/Src/IotaSharp/Core/AddNeighborsRequest.cs new file mode 100644 index 0000000..8975f81 --- /dev/null +++ b/Src/IotaSharp/Core/AddNeighborsRequest.cs @@ -0,0 +1,23 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class AddNeighborsRequest : IotaRequest + { + /// + /// + /// + /// + public AddNeighborsRequest(string[] uris) + : base(Core.Command.AddNeighbors) + { + Uris = uris; + } + + /// + /// + /// + public string[] Uris { get; } + } +} diff --git a/IotaApi.Standard/Core/AddNeighborsResponse.cs b/Src/IotaSharp/Core/AddNeighborsResponse.cs similarity index 83% rename from IotaApi.Standard/Core/AddNeighborsResponse.cs rename to Src/IotaSharp/Core/AddNeighborsResponse.cs index 21ac724..1b4861b 100644 --- a/IotaApi.Standard/Core/AddNeighborsResponse.cs +++ b/Src/IotaSharp/Core/AddNeighborsResponse.cs @@ -1,10 +1,9 @@ -namespace Iota.Api.Standard.Core +namespace IotaSharp.Core { - /// - /// Response of + /// /// - public class AddNeighborsResponse + public class AddNeighborsResponse : IotaResponse { /// /// Gets the number of added neighbors. @@ -25,4 +24,4 @@ public override string ToString() return $"{nameof(AddedNeighbors)}: {AddedNeighbors}"; } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Core/AddressRequest.cs b/Src/IotaSharp/Core/AddressRequest.cs new file mode 100644 index 0000000..2e2ccc4 --- /dev/null +++ b/Src/IotaSharp/Core/AddressRequest.cs @@ -0,0 +1,49 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class AddressRequest + { + /// + /// + /// + /// + /// + public AddressRequest(string seed, int securityLevel) + { + Seed = seed; + SecurityLevel = securityLevel; + } + + /// + /// + /// + public string Seed { get; } + + /// + /// + /// + public int SecurityLevel { get; } + + /// + /// + /// + public bool Checksum { get; set; } + + /// + /// + /// + public int Index { get; set; } + + /// + /// + /// + public int Amount { get; set; } + + /// + /// + /// + public bool AddSpendAddresses { get; set; } + } +} diff --git a/Src/IotaSharp/Core/AttachToTangleRequest.cs b/Src/IotaSharp/Core/AttachToTangleRequest.cs new file mode 100644 index 0000000..ceceb80 --- /dev/null +++ b/Src/IotaSharp/Core/AttachToTangleRequest.cs @@ -0,0 +1,56 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class AttachToTangleRequest : IotaRequest + { + /// + /// + /// + /// + /// + /// + /// + public AttachToTangleRequest( + string trunkTransaction, string branchTransaction, + int minWeightMagnitude, string[] trytes) + : base(Core.Command.AttachToTangle) + { + TrunkTransaction = trunkTransaction; + BranchTransaction = branchTransaction; + MinWeightMagnitude = minWeightMagnitude; + Trytes = trytes; + } + + /// + /// + /// + public string TrunkTransaction { get; } + + /// + /// + /// + public string BranchTransaction { get; } + + /// + /// + /// + public int MinWeightMagnitude { get; } + + /// + /// + /// + public string[] Trytes { get; } + + /// + /// + /// + /// + public override string ToString() + { + return + $"{nameof(MinWeightMagnitude)}: {MinWeightMagnitude}, {nameof(TrunkTransaction)}: {TrunkTransaction}, {nameof(BranchTransaction)}: {BranchTransaction}, {nameof(Trytes)}: {Trytes}"; + } + } +} diff --git a/Src/IotaSharp/Core/AttachToTangleResponse.cs b/Src/IotaSharp/Core/AttachToTangleResponse.cs new file mode 100644 index 0000000..6efd360 --- /dev/null +++ b/Src/IotaSharp/Core/AttachToTangleResponse.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class AttachToTangleResponse : IotaResponse + { + /// + /// + /// + public List Trytes { get; set; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; + } + } +} diff --git a/Src/IotaSharp/Core/BroadcastTransactionsRequest.cs b/Src/IotaSharp/Core/BroadcastTransactionsRequest.cs new file mode 100644 index 0000000..6ee7b3a --- /dev/null +++ b/Src/IotaSharp/Core/BroadcastTransactionsRequest.cs @@ -0,0 +1,23 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class BroadcastTransactionsRequest : IotaRequest + { + /// + /// + /// + /// + public BroadcastTransactionsRequest(string[] trytes) + : base(Core.Command.BroadcastTransactions) + { + Trytes = trytes; + } + + /// + /// + /// + public string[] Trytes { get; } + } +} diff --git a/Src/IotaSharp/Core/BroadcastTransactionsResponse.cs b/Src/IotaSharp/Core/BroadcastTransactionsResponse.cs new file mode 100644 index 0000000..d9154f7 --- /dev/null +++ b/Src/IotaSharp/Core/BroadcastTransactionsResponse.cs @@ -0,0 +1,9 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class BroadcastTransactionsResponse : IotaResponse + { + } +} diff --git a/Src/IotaSharp/Core/CheckConsistencyRequest.cs b/Src/IotaSharp/Core/CheckConsistencyRequest.cs new file mode 100644 index 0000000..cc268f9 --- /dev/null +++ b/Src/IotaSharp/Core/CheckConsistencyRequest.cs @@ -0,0 +1,31 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class CheckConsistencyRequest : IotaRequest + { + /// + /// + /// + /// + public CheckConsistencyRequest(string[] tails) : base(Core.Command.CheckConsistency) + { + Tails = tails; + } + + /// + /// + /// + public string[] Tails { get; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Tails)}: {string.Join(",", Tails)}"; + } + } +} diff --git a/Src/IotaSharp/Core/CheckConsistencyResponse.cs b/Src/IotaSharp/Core/CheckConsistencyResponse.cs new file mode 100644 index 0000000..de29d06 --- /dev/null +++ b/Src/IotaSharp/Core/CheckConsistencyResponse.cs @@ -0,0 +1,18 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class CheckConsistencyResponse : IotaResponse + { + /// + /// Gets the state + /// + public bool State { get; set; } + + /// + /// If state is false, this provides information on the cause of the inconsistency. + /// + public string Info { get; set; } + } +} diff --git a/Src/IotaSharp/Core/Command.cs b/Src/IotaSharp/Core/Command.cs new file mode 100644 index 0000000..79862a2 --- /dev/null +++ b/Src/IotaSharp/Core/Command.cs @@ -0,0 +1,89 @@ +namespace IotaSharp.Core +{ + /// + /// https://docs.iota.org/docs/iri/0.1/references/api-reference + /// + public enum Command + { + /// + /// + /// + GetNodeInfo, + + /// + /// + /// + GetTips, + + /// + /// + /// + FindTransactions, + + /// + /// + /// + GetTrytes, + + /// + /// + /// + GetInclusionStates, + + /// + /// + /// + GetTransactionsToApprove, + + /// + /// + /// + GetBalances, + + /// + /// + /// + WereAddressesSpentFrom, + + /// + /// + /// + CheckConsistency, + + /// + /// + /// + InterruptAttachingToTangle, + + /// + /// + /// + AttachToTangle, + + /// + /// + /// + BroadcastTransactions, + + /// + /// + /// + StoreTransactions, + + /// + /// + /// + GetNeighbors, + + /// + /// + /// + AddNeighbors, + + /// + /// + /// + RemoveNeighbors + + } +} diff --git a/Src/IotaSharp/Core/ErrorResponse.cs b/Src/IotaSharp/Core/ErrorResponse.cs new file mode 100644 index 0000000..adaf456 --- /dev/null +++ b/Src/IotaSharp/Core/ErrorResponse.cs @@ -0,0 +1,13 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class ErrorResponse + { + /// + /// + /// + public string Error { get; set; } + } +} diff --git a/Src/IotaSharp/Core/FindTransactionsRequest.cs b/Src/IotaSharp/Core/FindTransactionsRequest.cs new file mode 100644 index 0000000..3d7a97b --- /dev/null +++ b/Src/IotaSharp/Core/FindTransactionsRequest.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class FindTransactionsRequest : IotaRequest + { + /// + /// + /// + /// + /// + /// + /// + public FindTransactionsRequest( + List addresses, List tags, List approvees, List bundles) + : base(Core.Command.FindTransactions) + { + Addresses = addresses; + Tags = tags; + Approvees = approvees; + Bundles = bundles; + } + + /// + /// Gets or sets the addresses. + /// + /// + /// The addresses. + /// + public List Addresses { get; set; } + + /// + /// Gets or sets the tags. + /// + /// + /// The tags. + /// + public List Tags { get; set; } + + /// + /// Gets or sets the approvees. + /// + /// + /// The approvees. + /// + public List Approvees { get; set; } + + /// + /// Gets or sets the bundles. + /// + /// + /// The bundles. + /// + public List Bundles { get; set; } + + } +} diff --git a/Src/IotaSharp/Core/FindTransactionsResponse.cs b/Src/IotaSharp/Core/FindTransactionsResponse.cs new file mode 100644 index 0000000..0937cad --- /dev/null +++ b/Src/IotaSharp/Core/FindTransactionsResponse.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class FindTransactionsResponse: IotaResponse + { + /// + /// + /// + public List Hashes { get; set; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Hashes)}: {string.Join(",", Hashes)}"; + } + } +} diff --git a/Src/IotaSharp/Core/GetBalancesAndFormatResponse.cs b/Src/IotaSharp/Core/GetBalancesAndFormatResponse.cs new file mode 100644 index 0000000..64d71a2 --- /dev/null +++ b/Src/IotaSharp/Core/GetBalancesAndFormatResponse.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using IotaSharp.Model; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetBalancesAndFormatResponse : IotaResponse + { + /// + /// + /// + /// + /// + /// + public GetBalancesAndFormatResponse(List inputs, long totalBalance, long duration) + { + Inputs = inputs; + TotalBalance = totalBalance; + Duration = duration; + } + + /// + /// + /// + public List Inputs { get; } + + /// + /// + /// + public long TotalBalance { get; } + } +} diff --git a/Src/IotaSharp/Core/GetBalancesRequest.cs b/Src/IotaSharp/Core/GetBalancesRequest.cs new file mode 100644 index 0000000..22799a4 --- /dev/null +++ b/Src/IotaSharp/Core/GetBalancesRequest.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetBalancesRequest:IotaRequest + { + /// + /// + /// + /// + /// + /// + public GetBalancesRequest(long threshold, List addresses, List tips) + : base(Core.Command.GetBalances) + { + Addresses = addresses; + Threshold = threshold; + Tips = tips; + } + /// + /// + /// + public long Threshold { get; } + + /// + /// + /// + public List Addresses { get; } + + /// + /// + /// + public List Tips { get; } + } +} diff --git a/Src/IotaSharp/Core/GetBalancesResponse.cs b/Src/IotaSharp/Core/GetBalancesResponse.cs new file mode 100644 index 0000000..cee9ebe --- /dev/null +++ b/Src/IotaSharp/Core/GetBalancesResponse.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetBalancesResponse:IotaResponse + { + /// + /// + /// + public List Balances { get; set; } + + /// + /// + /// + public List References { get; set; } + + /// + /// + /// + public int MilestoneIndex { get; set; } + + /// + /// + /// + /// + public override string ToString() + { + return + $"{nameof(Balances)}: {string.Join(",", Balances)}, {nameof(References)}: {string.Join(",", References)}, {nameof(MilestoneIndex)}: {MilestoneIndex}"; + } + } +} diff --git a/Src/IotaSharp/Core/GetBundleResponse.cs b/Src/IotaSharp/Core/GetBundleResponse.cs new file mode 100644 index 0000000..022431d --- /dev/null +++ b/Src/IotaSharp/Core/GetBundleResponse.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using IotaSharp.Model; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetBundleResponse:IotaResponse + { + /// + /// + /// + /// + /// + public GetBundleResponse(List transactions, long duration) + { + Transactions = transactions; + Duration = duration; + } + + /// + /// + /// + public List Transactions { get; } + } +} diff --git a/Src/IotaSharp/Core/GetInclusionStatesRequest.cs b/Src/IotaSharp/Core/GetInclusionStatesRequest.cs new file mode 100644 index 0000000..f3cbece --- /dev/null +++ b/Src/IotaSharp/Core/GetInclusionStatesRequest.cs @@ -0,0 +1,39 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetInclusionStatesRequest : IotaRequest + { + /// + /// + /// + /// + /// + public GetInclusionStatesRequest(string[] transactions, string[] tips) + : base(Core.Command.GetInclusionStates) + { + Transactions = transactions; + Tips = tips; + } + + /// + /// + /// + public string[] Transactions { get; } + + /// + /// + /// + public string[] Tips { get; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Transactions)}: {Transactions}, {nameof(Tips)}: {Tips}"; + } + } +} diff --git a/Src/IotaSharp/Core/GetInclusionStatesResponse.cs b/Src/IotaSharp/Core/GetInclusionStatesResponse.cs new file mode 100644 index 0000000..2d05d60 --- /dev/null +++ b/Src/IotaSharp/Core/GetInclusionStatesResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetInclusionStatesResponse : IotaResponse + { + /// + /// + /// + public List States { get; set; } + } +} diff --git a/Src/IotaSharp/Core/GetNeighborsRequest.cs b/Src/IotaSharp/Core/GetNeighborsRequest.cs new file mode 100644 index 0000000..348e274 --- /dev/null +++ b/Src/IotaSharp/Core/GetNeighborsRequest.cs @@ -0,0 +1,15 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetNeighborsRequest : IotaRequest + { + /// + /// + /// + public GetNeighborsRequest() : base(Core.Command.GetNeighbors) + { + } + } +} diff --git a/Src/IotaSharp/Core/GetNeighborsResponse.cs b/Src/IotaSharp/Core/GetNeighborsResponse.cs new file mode 100644 index 0000000..4901af6 --- /dev/null +++ b/Src/IotaSharp/Core/GetNeighborsResponse.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using IotaSharp.Model; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetNeighborsResponse : IotaResponse + { + /// + /// + /// + public List Neighbors { get; set; } + } +} diff --git a/Src/IotaSharp/Core/GetNewAddressResponse.cs b/Src/IotaSharp/Core/GetNewAddressResponse.cs new file mode 100644 index 0000000..4cdad4b --- /dev/null +++ b/Src/IotaSharp/Core/GetNewAddressResponse.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetNewAddressResponse : IotaResponse + { + /// + /// + /// + public List Addresses { get; set; } + } +} diff --git a/Src/IotaSharp/Core/GetNodeInfoRequest.cs b/Src/IotaSharp/Core/GetNodeInfoRequest.cs new file mode 100644 index 0000000..0307e23 --- /dev/null +++ b/Src/IotaSharp/Core/GetNodeInfoRequest.cs @@ -0,0 +1,17 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetNodeInfoRequest : IotaRequest + { + /// + /// + /// + public GetNodeInfoRequest() + : base(Core.Command.GetNodeInfo) + { + + } + } +} diff --git a/IotaApi.Standard/Core/GetNodeInfoResponse.cs b/Src/IotaSharp/Core/GetNodeInfoResponse.cs similarity index 70% rename from IotaApi.Standard/Core/GetNodeInfoResponse.cs rename to Src/IotaSharp/Core/GetNodeInfoResponse.cs index 59c064b..fec2b61 100644 --- a/IotaApi.Standard/Core/GetNodeInfoResponse.cs +++ b/Src/IotaSharp/Core/GetNodeInfoResponse.cs @@ -1,9 +1,8 @@ -namespace Iota.Api.Standard.Core +namespace IotaSharp.Core { /// - /// This class represents the response of + /// /// - /// public class GetNodeInfoResponse : IotaResponse { /// @@ -90,17 +89,5 @@ public class GetNodeInfoResponse : IotaResponse /// The jre version. /// public string JreVersion { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return - $"{nameof(AppName)}: {AppName}, {nameof(AppVersion)}: {AppVersion}, {nameof(JreAvailableProcessors)}: {JreAvailableProcessors}, {nameof(JreFreeMemory)}: {JreFreeMemory}, {nameof(JreMaxMemory)}: {JreMaxMemory}, {nameof(JreTotalMemory)}: {JreTotalMemory}, {nameof(LatestMilestone)}: {LatestMilestone}, {nameof(LatestMilestoneIndex)}: {LatestMilestoneIndex}, {nameof(LatestSolidSubtangleMilestone)}: {LatestSolidSubtangleMilestone}, {nameof(LatestSolidSubtangleMilestoneIndex)}: {LatestSolidSubtangleMilestoneIndex}, {nameof(Neighbors)}: {Neighbors}, {nameof(PacketsQueueSize)}: {PacketsQueueSize}, {nameof(Time)}: {Time}, {nameof(Tips)}: {Tips}, {nameof(TransactionsToRequest)}: {TransactionsToRequest}"; - } } -} \ No newline at end of file +} diff --git a/IotaApi.Standard/Core/GetTipsRequest.cs b/Src/IotaSharp/Core/GetTipsRequest.cs similarity index 57% rename from IotaApi.Standard/Core/GetTipsRequest.cs rename to Src/IotaSharp/Core/GetTipsRequest.cs index 902ab4f..eb76ce3 100644 --- a/IotaApi.Standard/Core/GetTipsRequest.cs +++ b/Src/IotaSharp/Core/GetTipsRequest.cs @@ -1,15 +1,15 @@ -namespace Iota.Api.Standard.Core +namespace IotaSharp.Core { /// - /// This class represents the core API request 'GetTips' + /// /// public class GetTipsRequest : IotaRequest { /// /// Initializes a new instance of the class. /// - public GetTipsRequest() : base(Core.Command.GetTips.GetCommandString()) + public GetTipsRequest() : base(Core.Command.GetTips) { } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Core/GetTipsResponse.cs b/Src/IotaSharp/Core/GetTipsResponse.cs new file mode 100644 index 0000000..6358a7a --- /dev/null +++ b/Src/IotaSharp/Core/GetTipsResponse.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetTipsResponse: IotaResponse + { + /// + /// + /// + public List Hashes { get; set; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Hashes)}: {string.Join(",", Hashes)}"; + } + } +} diff --git a/Src/IotaSharp/Core/GetTransactionsToApproveRequest.cs b/Src/IotaSharp/Core/GetTransactionsToApproveRequest.cs new file mode 100644 index 0000000..e096e60 --- /dev/null +++ b/Src/IotaSharp/Core/GetTransactionsToApproveRequest.cs @@ -0,0 +1,39 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetTransactionsToApproveRequest:IotaRequest + { + /// + /// + /// + /// + /// + public GetTransactionsToApproveRequest(int depth, string reference) + : base(Core.Command.GetTransactionsToApprove) + { + Depth = depth; + Reference = reference; + } + + /// + /// + /// + public int Depth { get; } + + /// + /// + /// + public string Reference { get; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Depth)}: {Depth},{nameof(Reference)}: {Reference}"; + } + } +} diff --git a/Src/IotaSharp/Core/GetTransactionsToApproveResponse.cs b/Src/IotaSharp/Core/GetTransactionsToApproveResponse.cs new file mode 100644 index 0000000..c357320 --- /dev/null +++ b/Src/IotaSharp/Core/GetTransactionsToApproveResponse.cs @@ -0,0 +1,27 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetTransactionsToApproveResponse : IotaResponse + { + /// + /// + /// + public string TrunkTransaction { get; set; } + + /// + /// + /// + public string BranchTransaction { get; set; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(TrunkTransaction)}: {TrunkTransaction}, {nameof(BranchTransaction)}: {BranchTransaction}"; + } + } +} diff --git a/Src/IotaSharp/Core/GetTransferResponse.cs b/Src/IotaSharp/Core/GetTransferResponse.cs new file mode 100644 index 0000000..90d4d54 --- /dev/null +++ b/Src/IotaSharp/Core/GetTransferResponse.cs @@ -0,0 +1,26 @@ +using IotaSharp.Model; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetTransferResponse : IotaResponse + { + /// + /// + /// + /// + /// + public GetTransferResponse(Bundle[] transferBundles, long duration) + { + TransferBundles = transferBundles; + Duration = duration; + } + + /// + /// + /// + public Bundle[] TransferBundles { get; } + } +} diff --git a/Src/IotaSharp/Core/GetTrytesRequest.cs b/Src/IotaSharp/Core/GetTrytesRequest.cs new file mode 100644 index 0000000..313b7e9 --- /dev/null +++ b/Src/IotaSharp/Core/GetTrytesRequest.cs @@ -0,0 +1,21 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetTrytesRequest : IotaRequest + { + /// + /// + /// + public GetTrytesRequest(string[] hashes) : base(Core.Command.GetTrytes) + { + Hashes = hashes; + } + + /// + /// + /// + public string[] Hashes { get; } + } +} diff --git a/Src/IotaSharp/Core/GetTrytesResponse.cs b/Src/IotaSharp/Core/GetTrytesResponse.cs new file mode 100644 index 0000000..2cc092e --- /dev/null +++ b/Src/IotaSharp/Core/GetTrytesResponse.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class GetTrytesResponse:IotaResponse + { + /// + /// + /// + public List Trytes { get; set; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Trytes)}: {string.Join(",", Trytes)}"; + } + } +} diff --git a/Src/IotaSharp/Core/InterruptAttachingToTangleRequest.cs b/Src/IotaSharp/Core/InterruptAttachingToTangleRequest.cs new file mode 100644 index 0000000..8457bb5 --- /dev/null +++ b/Src/IotaSharp/Core/InterruptAttachingToTangleRequest.cs @@ -0,0 +1,15 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class InterruptAttachingToTangleRequest : IotaRequest + { + /// + /// + /// + public InterruptAttachingToTangleRequest() : base(Core.Command.InterruptAttachingToTangle) + { + } + } +} diff --git a/Src/IotaSharp/Core/InterruptAttachingToTangleResponse.cs b/Src/IotaSharp/Core/InterruptAttachingToTangleResponse.cs new file mode 100644 index 0000000..15621d2 --- /dev/null +++ b/Src/IotaSharp/Core/InterruptAttachingToTangleResponse.cs @@ -0,0 +1,9 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class InterruptAttachingToTangleResponse : IotaResponse + { + } +} diff --git a/Src/IotaSharp/Core/IotaRequest.cs b/Src/IotaSharp/Core/IotaRequest.cs new file mode 100644 index 0000000..d31bed1 --- /dev/null +++ b/Src/IotaSharp/Core/IotaRequest.cs @@ -0,0 +1,34 @@ +using System.Globalization; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class IotaRequest + { + /// + /// + /// + /// + public IotaRequest(Command command) + { + Command = FirstCharToLower(command.ToString()); + } + + /// + /// + /// + public string Command { get; set; } + + private string FirstCharToLower(string input) + { + if (string.IsNullOrEmpty(input)) + return input; + + var firstChar = char.ToLower(input[0], CultureInfo.InvariantCulture); + + return firstChar + input.Substring(1); + } + } +} diff --git a/Src/IotaSharp/Core/IotaResponse.cs b/Src/IotaSharp/Core/IotaResponse.cs new file mode 100644 index 0000000..24232a7 --- /dev/null +++ b/Src/IotaSharp/Core/IotaResponse.cs @@ -0,0 +1,13 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public abstract class IotaResponse + { + /// + /// + /// + public long Duration { get; set; } + } +} diff --git a/Src/IotaSharp/Core/RemoveNeighborsRequest.cs b/Src/IotaSharp/Core/RemoveNeighborsRequest.cs new file mode 100644 index 0000000..54ae677 --- /dev/null +++ b/Src/IotaSharp/Core/RemoveNeighborsRequest.cs @@ -0,0 +1,23 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class RemoveNeighborsRequest : IotaRequest + { + /// + /// + /// + /// + public RemoveNeighborsRequest(string[] uris) + : base(Core.Command.RemoveNeighbors) + { + Uris = uris; + } + + /// + /// + /// + public string[] Uris { get; } + } +} diff --git a/IotaApi.Standard/Core/RemoveNeighborsResponse.cs b/Src/IotaSharp/Core/RemoveNeighborsResponse.cs similarity index 80% rename from IotaApi.Standard/Core/RemoveNeighborsResponse.cs rename to Src/IotaSharp/Core/RemoveNeighborsResponse.cs index 834491d..e1e41da 100644 --- a/IotaApi.Standard/Core/RemoveNeighborsResponse.cs +++ b/Src/IotaSharp/Core/RemoveNeighborsResponse.cs @@ -1,9 +1,9 @@ -namespace Iota.Api.Standard.Core +namespace IotaSharp.Core { /// - /// This class represents the response of + /// /// - public class RemoveNeighborsResponse + public class RemoveNeighborsResponse : IotaResponse { /// /// Gets or sets the number of removed neighbors. @@ -24,4 +24,4 @@ public override string ToString() return $"{nameof(RemovedNeighbors)}: {RemovedNeighbors}"; } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Core/ReplayBundleResponse.cs b/Src/IotaSharp/Core/ReplayBundleResponse.cs new file mode 100644 index 0000000..17135e4 --- /dev/null +++ b/Src/IotaSharp/Core/ReplayBundleResponse.cs @@ -0,0 +1,33 @@ +using IotaSharp.Model; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class ReplayBundleResponse : IotaResponse + { + /// + /// + /// + /// + /// + /// + public ReplayBundleResponse(Bundle newBundle, bool[] successfully, long duration) + { + NewBundle = newBundle; + Successfully = successfully; + Duration = duration; + } + + /// + /// + /// + public bool[] Successfully { get; } + + /// + /// + /// + public Bundle NewBundle { get; } + } +} diff --git a/Src/IotaSharp/Core/SendTransferResponse.cs b/Src/IotaSharp/Core/SendTransferResponse.cs new file mode 100644 index 0000000..14eefcf --- /dev/null +++ b/Src/IotaSharp/Core/SendTransferResponse.cs @@ -0,0 +1,40 @@ +using System; +using System.Diagnostics.Contracts; +using IotaSharp.Model; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class SendTransferResponse : IotaResponse + { + /// + /// + /// + /// + /// + /// + public SendTransferResponse( + Transaction[] transactions, + bool[] successfully, + long duration) + { + Contract.Assert(transactions.Length == successfully.Length); + + int length = transactions.Length; + Results = new Tuple[length]; + for (int i = 0; i < length; i++) + { + Results[i] = new Tuple(transactions[i], successfully[i]); + } + + Duration = duration; + } + + /// + /// + /// + public Tuple[] Results { get; } + } +} diff --git a/Src/IotaSharp/Core/StoreTransactionsRequest.cs b/Src/IotaSharp/Core/StoreTransactionsRequest.cs new file mode 100644 index 0000000..7f63a31 --- /dev/null +++ b/Src/IotaSharp/Core/StoreTransactionsRequest.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class StoreTransactionsRequest : IotaRequest + { + /// + /// + /// + /// + public StoreTransactionsRequest(string[] trytes) + : base(Core.Command.StoreTransactions) + { + Trytes = trytes; + } + + /// + /// + /// + public string[] Trytes { get; } + } +} diff --git a/Src/IotaSharp/Core/StoreTransactionsResponse.cs b/Src/IotaSharp/Core/StoreTransactionsResponse.cs new file mode 100644 index 0000000..44ed1c5 --- /dev/null +++ b/Src/IotaSharp/Core/StoreTransactionsResponse.cs @@ -0,0 +1,9 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class StoreTransactionsResponse : IotaResponse + { + } +} diff --git a/Src/IotaSharp/Core/WereAddressesSpentFromRequest.cs b/Src/IotaSharp/Core/WereAddressesSpentFromRequest.cs new file mode 100644 index 0000000..dca1c15 --- /dev/null +++ b/Src/IotaSharp/Core/WereAddressesSpentFromRequest.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace IotaSharp.Core +{ + /// + /// + /// + public class WereAddressesSpentFromRequest : IotaRequest + { + /// + /// + /// + /// + public WereAddressesSpentFromRequest(List addresses) + : base(Core.Command.WereAddressesSpentFrom) + { + Addresses = addresses; + } + + /// + /// + /// + public List Addresses { get; } + + /// + /// + /// + /// + public override string ToString() + { + return $"{nameof(Addresses)}: {string.Join(",", Addresses)}"; + } + } +} diff --git a/Src/IotaSharp/Core/WereAddressesSpentFromResponse.cs b/Src/IotaSharp/Core/WereAddressesSpentFromResponse.cs new file mode 100644 index 0000000..a017e3e --- /dev/null +++ b/Src/IotaSharp/Core/WereAddressesSpentFromResponse.cs @@ -0,0 +1,13 @@ +namespace IotaSharp.Core +{ + /// + /// + /// + public class WereAddressesSpentFromResponse : IotaResponse + { + /// + /// + /// + public bool[] States { get; set; } + } +} diff --git a/Src/IotaSharp/Exception/IllegalAccessException.cs b/Src/IotaSharp/Exception/IllegalAccessException.cs new file mode 100644 index 0000000..75d9bf4 --- /dev/null +++ b/Src/IotaSharp/Exception/IllegalAccessException.cs @@ -0,0 +1,16 @@ +namespace IotaSharp.Exception +{ + /// + /// + /// + public class IllegalAccessException : System.Exception + { + /// + /// + /// + /// + public IllegalAccessException(string error) : base(error) + { + } + } +} diff --git a/IotaApi.Standard/Exception/IllegalStateException.cs b/Src/IotaSharp/Exception/IllegalStateException.cs similarity index 50% rename from IotaApi.Standard/Exception/IllegalStateException.cs rename to Src/IotaSharp/Exception/IllegalStateException.cs index b11f139..afeb626 100644 --- a/IotaApi.Standard/Exception/IllegalStateException.cs +++ b/Src/IotaSharp/Exception/IllegalStateException.cs @@ -1,17 +1,17 @@ -namespace Iota.Api.Standard.Exception +namespace IotaSharp.Exception { /// - /// This exception occurs when an illegal state is encountered + /// This exception occurs when an illegal state is encountered /// /// public class IllegalStateException : System.Exception { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error. - public IllegalStateException(string error):base(error) + public IllegalStateException(string error) : base(error) { } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Exception/IotaApiException.cs b/Src/IotaSharp/Exception/IotaApiException.cs new file mode 100644 index 0000000..66294f8 --- /dev/null +++ b/Src/IotaSharp/Exception/IotaApiException.cs @@ -0,0 +1,16 @@ +namespace IotaSharp.Exception +{ + /// + /// + /// + public class IotaApiException : System.Exception + { + /// + /// + /// + /// + public IotaApiException(string error) : base(error) + { + } + } +} diff --git a/Src/IotaSharp/IotaAPI.cs b/Src/IotaSharp/IotaAPI.cs new file mode 100644 index 0000000..3c7e3ef --- /dev/null +++ b/Src/IotaSharp/IotaAPI.cs @@ -0,0 +1,1293 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using IotaSharp.Core; +using IotaSharp.Exception; +using IotaSharp.Model; +using IotaSharp.Pow; +using IotaSharp.Utils; +using NLog; + +namespace IotaSharp +{ + /// + /// + /// + public class IotaAPI + { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + /// + /// + /// + public IotaClient IotaClient { get; set; } + + /// + /// + /// + public PearlDiverLocalPoW LocalPoW { get; set; } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public SendTransferResponse SendTransfer( + string seed, int security, + int depth, int minWeightMagnitude, + Transfer[] transfers, Input[] inputs, + string remainderAddress, + bool validateInputs, bool validateInputAddresses, + Transaction[] tips) + { + Stopwatch stopwatch = Stopwatch.StartNew(); + + var trytes = PrepareTransfers(seed, security, transfers, + remainderAddress, inputs, tips, validateInputs); + + if (validateInputAddresses) + ValidateTransfersAddresses(seed, security, trytes); + + string reference = tips != null && tips.Length > 0 ? tips[0].CurlHash() : null; + + var transactions = SendTrytes(trytes.ToArray(), depth, minWeightMagnitude, reference); + + var successful = new bool[transactions.Length]; + + for (var i = 0; i < transactions.Length; i++) + { + var response = IotaClient.FindTransactionsByBundles(transactions[i].Bundle); + + successful[i] = response.Hashes.Count != 0; + } + + stopwatch.Stop(); + + return new SendTransferResponse(transactions, successful, stopwatch.ElapsedMilliseconds); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public List PrepareTransfers( + string seed, int security, + Transfer[] transfers, + string remainder, + Input[] inputs, + Transaction[] tips, + bool validateInputs) + { + // validate seed + if (!InputValidator.IsValidSeed(seed)) + throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR); + + + if (!InputValidator.IsValidSecurityLevel(security)) + throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR); + + if (remainder != null && !InputValidator.IsAddress(remainder)) + { + throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR); + } + + // Input validation of transfers object + if (!InputValidator.IsValidTransfersCollection(transfers.ToList())) + { + throw new ArgumentException(Constants.INVALID_TRANSFERS_INPUT_ERROR); + } + + if (inputs != null && !InputValidator.IsValidInputsCollection(inputs)) + { + throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR); + } + + // Create a new bundle + var bundle = new Bundle(); + var signatureFragments = new List(); + + long totalValue = 0; + var tag = ""; + + // + // Iterate over all transfers, get totalValue + // and prepare the signatureFragments, message and tag + // + foreach (var transfer in transfers) + { + // remove the checksum of the address if provided + transfer.Address = transfer.Address.RemoveChecksum(); + + var signatureMessageLength = 1; + + // If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction) + if (transfer.Message.Length > Constants.MESSAGE_LENGTH) + { + // Get total length, message / maxLength (2187 trytes) + signatureMessageLength += + (int) Math.Floor((double) transfer.Message.Length / Constants.MESSAGE_LENGTH); + + var msgCopy = transfer.Message; + + // While there is still a message, copy it + while (!string.IsNullOrEmpty(msgCopy)) + { + var fragment = msgCopy.Substring(0, 2187 > msgCopy.Length ? msgCopy.Length : 2187); + msgCopy = msgCopy.Substring(fragment.Length, msgCopy.Length - fragment.Length); + + // Pad remainder of fragment + if (fragment.Length < 2187) + fragment = fragment.PadRight(2187, '9'); + + signatureFragments.Add(fragment); + } + } + else + { + // Else, get single fragment with 2187 of 9's trytes + var fragment = transfer.Message.PadRight(Constants.MESSAGE_LENGTH, '9'); + + signatureFragments.Add(fragment); + } + + // get current timestamp in seconds + var timestamp = (long) Math.Floor((double) TimeStamp.Now() / 1000); + + // If no tag defined, get 27 tryte tag. + + tag = string.IsNullOrEmpty(transfer.Tag) ? "999999999999999999999999999" : transfer.Tag; + + // Pad for required 27 tryte length + if (tag.Length < Constants.TAG_LENGTH) + tag = tag.PadRight(Constants.TAG_LENGTH, '9'); + + // Add first entries to the bundle + // Slice the address in case the user provided a checksummed one + bundle.AddEntry(signatureMessageLength, transfer.Address, transfer.Value, tag, timestamp); + // Sum up total value + totalValue += transfer.Value; + } + + // Get inputs if we are sending tokens + if (totalValue != 0) + { + // validate seed + if (!InputValidator.IsValidSeed(seed)) + throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR); + + + // Case 1: user provided inputs + // Validate the inputs by calling getBalances + if (inputs != null && inputs.Length > 0) + { + if (!validateInputs) + { + return AddRemainder(seed, security, inputs.ToList(), bundle, tag, totalValue, remainder, + signatureFragments); + } + + // Get list if addresses of the provided inputs + var inputAddresses = new List(); + foreach (var input in inputs) inputAddresses.Add(input.Address); + + List tipHashes = null; + if (tips != null) + { + tipHashes = new List(); + foreach (var tx in tips) + { + tipHashes.Add(tx.CurlHash()); + } + } + + var balances = IotaClient.GetBalances(100, inputAddresses, tipHashes); + + var confirmedInputs = new List(); + + long totalBalance = 0; + for (var i = 0; i < balances.Balances.Count; i++) + { + var thisBalance = balances.Balances[i]; + totalBalance += thisBalance; + + // If input has balance, add it to confirmedInputs + if (thisBalance > 0) + { + var inputEl = inputs[i]; + inputEl.Balance = thisBalance; + + confirmedInputs.Add(inputEl); + + // if we've already reached the intended input value, break out of loop + if (totalBalance >= totalValue) + { + Log.Info("Total balance already reached "); + break; + } + } + } + + // Return not enough balance error + if (totalValue > totalBalance) throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR); + + return AddRemainder(seed, security, confirmedInputs, bundle, tag, totalValue, remainder, + signatureFragments); + } + + // Case 2: Get inputs deterministically + // + // If no inputs provided, derive the addresses from the seed and + // confirm that the inputs exceed the threshold + var inputList = GetInputs(seed, security, 0, 0, (int) totalValue).Inputs; + return AddRemainder(seed, security, inputList, bundle, tag, totalValue, remainder, + signatureFragments); + } + + // If no input required, don't sign and simply finalize the bundle + bundle.FinalizeBundle(SpongeFactory.Create(SpongeFactory.Mode.KERL)); + bundle.AddTrytes(signatureFragments); + + var bundleTrytes = new List(); + bundle.Transactions.ForEach(tx => bundleTrytes.Add(tx.ToTrytes())); + + bundleTrytes.Reverse(); + return bundleTrytes; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Transaction[] SendTrytes(string[] trytes, int depth, int minWeightMagnitude, string reference) + { + var transactionsToApproveResponse = IotaClient.GetTransactionsToApprove(depth, reference); + + // attach to tangle - do pow + AttachToTangleResponse attachToTangleResponse; + if (LocalPoW == null) + { + attachToTangleResponse = + IotaClient.AttachToTangle(transactionsToApproveResponse.TrunkTransaction, + transactionsToApproveResponse.BranchTransaction, minWeightMagnitude, trytes); + } + else + { + attachToTangleResponse = + LocalPoW.AttachToTangle(transactionsToApproveResponse.TrunkTransaction, + transactionsToApproveResponse.BranchTransaction, minWeightMagnitude, trytes); + } + + try + { + BroadcastAndStore(attachToTangleResponse.Trytes.ToArray()); + } + catch (System.Exception) + { + return Array.Empty(); + } + + var trx = new List(); + + foreach (var tx in attachToTangleResponse.Trytes) trx.Add(new Transaction(tx)); + return trx.ToArray(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public GetBalancesAndFormatResponse GetInputs(string seed, int security, int start, int end, long threshold, + params string[] tips) + { + // validate the seed + if (!InputValidator.IsValidSeed(seed)) + { + throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR); + } + + seed = InputValidator.PadSeedIfNecessary(seed); + + if (!InputValidator.IsValidSecurityLevel(security)) + throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR); + + // If start value bigger than end, return error + if (start > end) + throw new ArgumentException("start must be smaller than end", nameof(start)); + + // or if difference between end and start is bigger than 500 keys + if (end - start > 500) + throw new ArgumentException("total number of keys exceeded 500"); + + + Stopwatch stopwatch = Stopwatch.StartNew(); + + // Case 1: start and end + // + // If start and end is defined by the user, simply iterate through the keys + // and call getBalances + if (end != 0) + { + var allAddresses = new string[end - start]; + + for (var i = start; i < end; i++) + { + var address = IotaApiUtils.NewAddress(seed, security, i, true, + SpongeFactory.Create(SpongeFactory.Mode.KERL)); + allAddresses[i] = address; + } + + return GetBalanceAndFormat(allAddresses, tips, threshold, start, security, stopwatch); + } + + { + // Case 2: iterate till threshold + // + // Either start from index: 0 or start (if defined) until threshold is reached. + List allInputs = new List(); + + bool thresholdReached = true; + long currentTotal = 0; + + for (int i = start; thresholdReached; i++) + { + + string address = IotaApiUtils.NewAddress(seed, security, i, true, + SpongeFactory.Create(SpongeFactory.Mode.KERL)); + + // Received input, this epoch or previous + GetBalancesResponse response = + IotaClient.GetBalances(100, new List() {address}, tips.ToList()); + var balance = response.Balances[0]; + + if (balance > 0) + { + // Is it already spent from? + WereAddressesSpentFromResponse wasSpent = IotaClient.WereAddressesSpentFrom(address); + if (wasSpent.States.Length > 0 && !wasSpent.States[0]) + { + // We can use this! + allInputs.Add(new Input + { + Address = address, Balance = balance, KeyIndex = i, Security = security + }); + currentTotal += balance; + + if (threshold != 0 && threshold <= currentTotal) + { + // Stop because we found threshold + thresholdReached = false; + } + } + } + else + { + // Check if there was any activity at all + FindTransactionsResponse tx = IotaClient.FindTransactionsByAddresses(address); + if (tx.Hashes.Count == 0 || i - start > 500) + { + // Stop because we reached our limit or no activity + thresholdReached = false; + } + } + + } + + stopwatch.Stop(); + return new GetBalancesAndFormatResponse(allInputs, currentTotal, stopwatch.ElapsedMilliseconds); + } + } + + /// + /// + /// + /// + public void BroadcastAndStore(params string[] trytes) + { + IotaClient.BroadcastTransactions(trytes); + IotaClient.StoreTransactions(trytes); + } + + private void ValidateTransfersAddresses(string seed, int security, List trytes) + { + HashSet addresses = new HashSet(); + List inputTransactions = new List(); + List inputAddresses = new List(); + + + foreach (var trx in trytes) + { + addresses.Add(new Transaction(trx).Address); + inputTransactions.Add(new Transaction(trx)); + } + + var hashes = IotaClient.FindTransactionsByAddresses(addresses.ToArray()).Hashes; + List transactions = FindTransactionObjectsByHashes(hashes.ToArray()); + + // Get addresses until first unspent + var addressRequest = new AddressRequest(seed, security) {Amount = 0, Checksum = true}; + var gna = GenerateNewAddresses(addressRequest); + + // Get inputs for this seed, until we fund an unused address + var gbr = GetInputs(seed, security, 0, 0, 0); + + foreach (var input in gbr.Inputs) + { + inputAddresses.Add(input.Address); + } + + //check if send to input + foreach (var trx in inputTransactions) + { + if (trx.Value > 0 && inputAddresses.Contains(trx.Address)) + throw new ArgumentException("Send to inputs!"); + } + + foreach (var trx in transactions) + { + //check if destination address is already in use + if (trx.Value < 0 && !inputAddresses.Contains(trx.Address)) + { + throw new ArgumentException("Sending to a used address."); + } + + //check if key reuse + if (trx.Value < 0 && gna.Addresses.Contains(trx.Address)) + { + throw new ArgumentException("Private key reuse detect!"); + } + + } + + } + + /// + /// + /// + /// + /// + public List FindTransactionObjectsByHashes(string[] hashes) + { + if (!InputValidator.IsArrayOfHashes(hashes)) + throw new IllegalStateException("Not an Array of Hashes: " + hashes); + + var trytesResponse = IotaClient.GetTrytes(hashes); + + var trxs = new List(); + + foreach (var tryte in trytesResponse.Trytes) trxs.Add(new Transaction(tryte)); + return trxs; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public List InitiateTransfer( + int securitySum, string inputAddress, string remainderAddress, + List transfers, List tips, bool testMode) + { + // validate input address + if (!InputValidator.IsAddress(inputAddress)) + throw new ArgumentException("Invalid addresses provided."); + + // validate remainder address + if (remainderAddress != null && !InputValidator.IsAddress(remainderAddress)) + { + throw new ArgumentException("Invalid addresses provided."); + } + + // Input validation of transfers object + if (!InputValidator.IsValidTransfersCollection(transfers)) + { + throw new ArgumentException("Invalid transfers provided."); + } + + // Create a new bundle + Bundle bundle = new Bundle(); + + long totalValue = 0; + List signatureFragments = new List(); + String tag = ""; + // + + // Iterate over all transfers, get totalValue + // and prepare the signatureFragments, message and tag + foreach (Transfer transfer in transfers) + { + + // remove the checksum of the address if provided + if (transfer.Address.IsValidChecksum()) + { + transfer.Address = transfer.Address.RemoveChecksum(); + } + + int signatureMessageLength = 1; + + // If message longer than 2187 trytes, increase signatureMessageLength (add next transaction) + if (transfer.Message.Length > Constants.MESSAGE_LENGTH) + { + + // Get total length, message / maxLength (2187 trytes) + signatureMessageLength += + (int) Math.Floor((double) transfer.Message.Length / Constants.MESSAGE_LENGTH); + + String msgCopy = transfer.Message; + + // While there is still a message, copy it + + while (!string.IsNullOrEmpty(msgCopy)) + { + + string fragment = msgCopy.Substring(0, Constants.MESSAGE_LENGTH); + msgCopy = msgCopy.Substring(Constants.MESSAGE_LENGTH, + msgCopy.Length - Constants.MESSAGE_LENGTH); + + // Pad remainder of fragment + fragment = fragment.PadRight(Constants.MESSAGE_LENGTH, '9'); + + + signatureFragments.Add(fragment); + } + + } + else + { + + // Else, get single fragment with 2187 of 9's trytes + String fragment = transfer.Message; + + if (transfer.Message.Length < Constants.MESSAGE_LENGTH) + { + fragment = fragment.PadRight(Constants.MESSAGE_LENGTH, '9'); + } + + signatureFragments.Add(fragment); + + } + + tag = transfer.Tag; + + // pad for required 27 tryte length + if (transfer.Tag.Length < Constants.TAG_LENGTH) + { + tag = tag.PadRight(Constants.TAG_LENGTH, '9'); + } + + // get current timestamp in seconds + long timestamp = (long) Math.Floor(GetCurrentTimestampInSeconds()); + + // Add first entry to the bundle + bundle.AddEntry(signatureMessageLength, transfer.Address, transfer.Value, tag, timestamp); + // Sum up total value + totalValue += transfer.Value; + } + + // Get inputs if we are sending tokens + if (totalValue != 0) + { + List tipHashes = null; + if (tips != null) + { + tipHashes = new List(); + foreach (var tx in tips) + { + tipHashes.Add(tx.CurlHash()); + } + } + + + GetBalancesResponse balancesResponse = + IotaClient.GetBalances(100, new List {inputAddress}, tipHashes); + var balances = balancesResponse.Balances; + + long totalBalance = 0; + + foreach (var balance in balances) + { + totalBalance += balance; + } + + // get current timestamp in seconds + long timestamp = (long) Math.Floor(GetCurrentTimestampInSeconds()); + + // bypass the balance checks during unit testing + //TODO remove this ugliness + if (testMode) + totalBalance += 1000; + + if (totalBalance > 0) + { + + long toSubtract = 0 - totalBalance; + + // Add input as bundle entry + // Only a single entry, signatures will be added later + bundle.AddEntry(securitySum, inputAddress, toSubtract, tag, timestamp); + } + + // Return not enough balance error + if (totalValue > totalBalance) + { + throw new IllegalStateException("Not enough balance."); + } + + // If there is a remainder value + // Add extra output to send remaining funds to + if (totalBalance > totalValue) + { + + long remainder = totalBalance - totalValue; + + // Remainder bundle entry if necessary + if (remainderAddress == null) + { + throw new IllegalStateException("No remainder address defined."); + } + + bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp); + } + + bundle.FinalizeBundle(SpongeFactory.Create(SpongeFactory.Mode.CURLP81)); + bundle.AddTrytes(signatureFragments); + + return bundle.Transactions; + } + else + { + throw new System.Exception("Invalid value transfer: the transfer does not require a signature."); + } + } + + private List AddRemainder( + string seed, + int security, + List inputs, + Bundle bundle, + string tag, + long totalValue, + string remainderAddress, + List signatureFragments) + { + var totalTransferValue = totalValue; + + foreach (var input in inputs) + { + var thisBalance = input.Balance; + var toSubtract = 0 - thisBalance; + var timestamp = TimeStamp.Now(); + + // Add input as bundle entry + // use input.Security + bundle.AddEntry(input.Security, input.Address, toSubtract, tag, timestamp); + // If there is a remainder value + // Add extra output to send remaining funds to + + if (thisBalance >= totalTransferValue) + { + var remainder = thisBalance - totalTransferValue; + + // If user has provided remainder address + // Use it to send remaining funds to + if (remainder > 0 && remainderAddress != null) + { + // Remainder bundle entry + bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp); + + // function for signing inputs + return IotaApiUtils.SignInputsAndReturn( + seed, inputs, bundle, signatureFragments, + SpongeFactory.Create(SpongeFactory.Mode.KERL)); + } + + if (remainder > 0) + { + AddressRequest addressRequest = new AddressRequest(seed, security); + var res = GenerateNewAddresses(addressRequest); + + // Remainder bundle entry + bundle.AddEntry(1, res.Addresses[0], remainder, tag, timestamp); + + // function for signing inputs + return IotaApiUtils.SignInputsAndReturn( + seed, inputs, bundle, signatureFragments, + SpongeFactory.Create(SpongeFactory.Mode.KERL)); + } + + // If there is no remainder, do not add transaction to bundle + // simply sign and return + return IotaApiUtils.SignInputsAndReturn( + seed, inputs, bundle, signatureFragments, + SpongeFactory.Create(SpongeFactory.Mode.KERL)); + } + + // If multiple inputs provided, subtract the totalTransferValue by + // the inputs balance + totalTransferValue -= thisBalance; + } + + throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR); + } + + private GetBalancesAndFormatResponse GetBalanceAndFormat( + string[] addresses, + string[] tips, + long threshold, + int start, + int security, + Stopwatch stopwatch = null) + { + if (stopwatch == null) + stopwatch = Stopwatch.StartNew(); + + if (!InputValidator.IsValidSecurityLevel(security)) + throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR); + + var getBalancesResponse = IotaClient.GetBalances(100, addresses.ToList(), tips.ToList()); + + var balances = getBalancesResponse.Balances; + + // If threshold defined, keep track of whether reached or not + // else set default to true + + var inputList = new List(); + long totalBalance = 0; + + var thresholdReached = threshold == 0; + + for (var i = 0; i < addresses.Length; i++) + if (balances[i] > 0) + { + inputList.Add(new Input + { + Address = addresses[i], + Balance = balances[i], + KeyIndex = start + i, + Security = security + }); + + totalBalance += balances[i]; + + if (totalBalance >= threshold) + { + thresholdReached = true; + break; + } + } + + stopwatch.Stop(); + if (thresholdReached) + return new GetBalancesAndFormatResponse(inputList, totalBalance, stopwatch.ElapsedMilliseconds); + + throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR); + } + + private double GetCurrentTimestampInSeconds() + { + DateTime now = DateTime.UtcNow; + DateTime epoch = new DateTime + (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + return (now - epoch).TotalSeconds; + } + + /// + /// This does not mean that these addresses are safe to use (unspent) + /// + /// + /// + public GetNewAddressResponse GetAddressesUnchecked(AddressRequest addressRequest) + { + var stopwatch = Stopwatch.StartNew(); + + List addresses = new List(); + + for (int i = 0; i < addressRequest.Amount; i++) + addresses.Add( + IotaApiUtils.NewAddress( + addressRequest.Seed, + addressRequest.SecurityLevel, i, + addressRequest.Checksum, + SpongeFactory.Create(SpongeFactory.Mode.KERL))); + + stopwatch.Stop(); + + return new GetNewAddressResponse + { + Addresses = addresses, + Duration = stopwatch.ElapsedMilliseconds + }; + } + + /// + /// Generates new addresses, meaning addresses which were not spend from, according to the connected node. + /// + /// + /// + public GetNewAddressResponse GenerateNewAddresses(AddressRequest addressRequest) + { + var stopwatch = Stopwatch.StartNew(); + + List addresses; + + if (addressRequest.Amount == 0) + { + string unusedAddress = GetFirstUnusedAddress( + addressRequest.Seed, addressRequest.SecurityLevel, + addressRequest.Index, addressRequest.Checksum); + + addresses = new List + { + unusedAddress + }; + + } + else + { + addresses = GetAddresses( + addressRequest.Seed, addressRequest.SecurityLevel, + addressRequest.Index, addressRequest.Checksum, + addressRequest.Amount, addressRequest.AddSpendAddresses); + } + + stopwatch.Stop(); + return new GetNewAddressResponse + { + Addresses = addresses, + Duration = stopwatch.ElapsedMilliseconds + }; + } + + private List GetAddresses(string seed, int securityLevel, int index, bool checksum, int amount, + bool addSpendAddresses) + { + List addresses = new List(); + + for (int i = index, numUnspentFound = 0; numUnspentFound < amount; i++) + { + string newAddress = IotaApiUtils.NewAddress(seed, securityLevel, i, checksum, + SpongeFactory.Create(SpongeFactory.Mode.KERL)); + + if (!IsAddressSpent(newAddress, checksum)) + { + addresses.Add(newAddress); + numUnspentFound++; + } + else if (addSpendAddresses) + { + addresses.Add(newAddress); + } + } + + return addresses; + } + + private string GetFirstUnusedAddress(string seed, int securityLevel, int index, bool checksum) + { + while (true) + { + string newAddress = + IotaApiUtils.NewAddress( + seed, securityLevel, index, checksum, + SpongeFactory.Create(SpongeFactory.Mode.KERL)); + if (!IsAddressSpent(newAddress, checksum)) + { + return newAddress; + } + + index++; + } + } + + private bool IsAddressSpent(string newAddress, bool checksum) + { + string address = checksum ? newAddress : newAddress.AddChecksum(); + var response = IotaClient.FindTransactionsByAddresses(address); + + if (response.Hashes.Count == 0) + { + var spentFromResponse = IotaClient.WereAddressesSpentFrom(address); + return spentFromResponse.States[0]; + } + + return true; + } + + /// + /// + /// + /// + /// + public GetBundleResponse GetBundle(string transaction) + { + if (!InputValidator.IsHash(transaction)) + { + throw new ArgumentException(Constants.INVALID_HASHES_INPUT_ERROR); + } + + var stopWatch = Stopwatch.StartNew(); + + Bundle bundle = TraverseBundle(transaction, null, new Bundle()); + if (bundle == null) + { + throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR); + } + + if (!BundleValidator.IsBundle(bundle)) + { + throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR); + } + + stopWatch.Stop(); + return new GetBundleResponse(bundle.Transactions, stopWatch.ElapsedMilliseconds); + } + + private Bundle TraverseBundle(string trunkTx, string bundleHash, Bundle bundle) + { + var gtr = IotaClient.GetTrytes(trunkTx); + + if (gtr != null) + { + + if (gtr.Trytes.Count == 0) + { + throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR); + } + + Transaction trx = new Transaction(gtr.Trytes[0]); + if (trx.Bundle == null) + { + throw new ArgumentException(Constants.INVALID_TRYTES_INPUT_ERROR); + } + + // If first transaction to search is not a tail, return error + if (bundleHash == null && trx.CurrentIndex != 0) + { + throw new ArgumentException(Constants.INVALID_TAIL_HASH_INPUT_ERROR); + } + + // If no bundle hash, define it + if (bundleHash == null) + { + bundleHash = trx.Bundle; + } + + // If different bundle hash, return with bundle + if (!bundleHash.Equals(trx.Bundle, StringComparison.Ordinal)) + { + bundle.Length = bundle.Transactions.Count; + return bundle; + } + + // If only one bundle element, return + if (trx.LastIndex == 0 && trx.CurrentIndex == 0) + { + return new Bundle(new List {trx}, 1); + } + + // Define new trunkTransaction for search + trunkTx = trx.TrunkTransaction; + // Add transaction object to bundle + bundle.Transactions.Add(trx); + + // Continue traversing with new trunkTx + return TraverseBundle(trunkTx, bundleHash, bundle); + } + + throw new ArgumentException(Constants.GET_TRYTES_RESPONSE_ERROR); + } + + /// + /// + /// + /// + /// + public GetInclusionStatesResponse GetLatestInclusion(string[] hashes) + { + string[] latestMilestone = {IotaClient.GetNodeInfo().LatestSolidSubtangleMilestone}; + return IotaClient.GetInclusionStates(hashes, latestMilestone); + } + + /// + /// + /// + /// + /// + public List FindTransactionObjectsByAddresses(string[] addresses) + { + var ftr = IotaClient.FindTransactionsByAddresses(addresses); + if (ftr?.Hashes == null) + { + return new List(); + } + + // get the transaction objects of the transactions + return FindTransactionObjectsByHashes(ftr.Hashes.ToArray()); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public GetTransferResponse GetTransfers(string seed, int security, int start, int end, bool inclusionStates) + { + // validate seed + if (!InputValidator.IsValidSeed(seed)) + throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR); + + if (start < 0 || start > end || end > (start + 500)) + throw new ArgumentException(Constants.INVALID_INPUT_ERROR); + + if (!InputValidator.IsValidSecurityLevel(security)) + throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR); + + var stopWatch = Stopwatch.StartNew(); + + var addressRequest = new AddressRequest(seed, security) + { + Index = start, + Checksum = true, + Amount = end, + AddSpendAddresses = true + }; + + var gnr = GenerateNewAddresses(addressRequest); + + if (gnr?.Addresses != null) + { + var bundles = BundlesFromAddresses(inclusionStates, gnr.Addresses.ToArray()); + + stopWatch.Stop(); + return new GetTransferResponse(bundles, stopWatch.ElapsedMilliseconds); + } + + stopWatch.Stop(); + return new GetTransferResponse(Array.Empty(), stopWatch.ElapsedMilliseconds); + } + + /// + /// + /// + /// + /// + /// + public Bundle[] BundlesFromAddresses(bool inclusionStates, params string[] addresses) + { + List trxs = FindTransactionObjectsByAddresses(addresses); + // set of tail transactions + List tailTransactions = new List(); + List nonTailBundleHashes = new List(); + + foreach (var trx in trxs) + { + // Sort tail and nonTails + if (trx.CurrentIndex == 0) + { + tailTransactions.Add(trx.CurlHash()); + } + else + { + if (nonTailBundleHashes.IndexOf(trx.Bundle) == -1) + { + nonTailBundleHashes.Add(trx.Bundle); + } + } + } + + List bundleObjects = + FindTransactionObjectsByBundle(nonTailBundleHashes.ToArray()); + + foreach (var trx in bundleObjects) + { + // Sort tail and nonTails + if (trx.CurrentIndex == 0) + { + var hash = trx.CurlHash(); + if (tailTransactions.IndexOf(hash) == -1) + { + tailTransactions.Add(hash); + } + } + } + + List finalBundles = new List(); + var tailTxArray = tailTransactions.ToArray(); + + // If inclusionStates, get the confirmation status + // of the tail transactions, and thus the bundles + GetInclusionStatesResponse gisr = null; + if (tailTxArray.Length != 0 && inclusionStates) + { + gisr = GetLatestInclusion(tailTxArray); + if (gisr?.States == null || gisr.States.Count == 0) + { + throw new IllegalStateException(Constants.GET_INCLUSION_STATE_RESPONSE_ERROR); + } + } + + GetInclusionStatesResponse finalInclusionStates = gisr; + try + { + Parallel.ForEach(tailTxArray, tailTx => + { + try + { + GetBundleResponse bundleResponse = GetBundle(tailTx); + Bundle gbr = new Bundle(bundleResponse.Transactions, + bundleResponse.Transactions.Count); + if (gbr.Transactions != null) + { + if (inclusionStates) + { + bool thisInclusion = false; + if (finalInclusionStates != null) + { + thisInclusion = finalInclusionStates.States[tailTxArray.ToList().IndexOf(tailTx)]; + } + + foreach (var t in gbr.Transactions) + { + t.Persistence = thisInclusion; + } + } + + finalBundles.Add(gbr); + } + + // If error returned from getBundle, simply ignore it because the bundle was most likely incorrect + } + catch (ArgumentException) + { + Log.Warn(Constants.GET_BUNDLE_RESPONSE_ERROR); + } + }); + } + catch (AggregateException) + { + return null; + } + + finalBundles.Sort(); + Bundle[] returnValue = new Bundle[finalBundles.Count]; + for (int i = 0; i < finalBundles.Count; i++) + { + returnValue[i] = new Bundle(finalBundles[i].Transactions, + finalBundles[i].Transactions.Count); + } + + return returnValue; + + } + + /// + /// + /// + /// + /// + public List FindTransactionObjectsByBundle(params string[] bundles) + { + var ftr = IotaClient.FindTransactionsByBundles(bundles); + if (ftr?.Hashes == null) + { + return new List(); + } + + // get the transaction objects of the transactions + return FindTransactionObjectsByHashes(ftr.Hashes.ToArray()); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public ReplayBundleResponse ReplayBundle(string tailTransactionHash, int depth, int minWeightMagnitude, + string reference) + { + if (!InputValidator.IsHash(tailTransactionHash)) + { + throw new ArgumentException(Constants.INVALID_TAIL_HASH_INPUT_ERROR); + } + + var stopWatch = Stopwatch.StartNew(); + + GetBundleResponse bundleResponse = GetBundle(tailTransactionHash); + Bundle bundle = new Bundle(bundleResponse.Transactions, bundleResponse.Transactions.Count); + return ReplayBundle(bundle, depth, minWeightMagnitude, reference, stopWatch); + + } + + private ReplayBundleResponse ReplayBundle(Bundle bundle, int depth, int minWeightMagnitude, string reference, + Stopwatch stopWatch) + { + + List bundleTrytes = new List(); + + foreach (var trx in bundle.Transactions) + { + bundleTrytes.Add(trx.ToTrytes()); + } + + bundleTrytes.Reverse(); + + + var trxs = SendTrytes(bundleTrytes.ToArray(), depth, minWeightMagnitude, reference); + + bool[] successful = new bool[trxs.Length]; + + for (int i = 0; i < trxs.Length; i++) + { + + var response = IotaClient.FindTransactionsByBundles(trxs[i].Bundle); + + successful[i] = response.Hashes.Count != 0; + } + + stopWatch.Stop(); + return new ReplayBundleResponse(new Bundle(trxs.ToList(), trxs.Length), successful, + stopWatch.ElapsedMilliseconds); + + } + } +} diff --git a/Src/IotaSharp/IotaClient.cs b/Src/IotaSharp/IotaClient.cs new file mode 100644 index 0000000..eeb3c7e --- /dev/null +++ b/Src/IotaSharp/IotaClient.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using IotaSharp.Core; +using IotaSharp.Utils; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace IotaSharp +{ + /// + /// + /// + public class IotaClient + { + private readonly JsonWebClient _jsonWebClient; + private readonly JsonSerializerSettings _jsonSerializerSettings; + + /// + /// + /// + /// + public IotaClient(string uriString) + { + _jsonWebClient = new JsonWebClient(); + _jsonSerializerSettings = new JsonSerializerSettings + { + MissingMemberHandling = MissingMemberHandling.Ignore, + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Include, + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + UriString = uriString; + } + + /// + /// + /// + public string UriString { get; set; } + + /// + /// + /// + /// + public GetNodeInfoResponse GetNodeInfo() + { + return Request(new GetNodeInfoRequest()); + } + + /// + /// + /// + /// + public GetTipsResponse GetTips() + { + return Request(new GetTipsRequest()); + } + + /// + /// + /// + /// without checksum + /// + /// + /// + /// + public FindTransactionsResponse FindTransactions( + List addresses, List tags, + List approvees, List bundles) + { + if (addresses != null) + { + for (int i = 0; i < addresses.Count; i++) + { + addresses[i] = addresses[i].RemoveChecksum(); + } + } + + if (tags != null) + { + foreach (var tag in tags) + { + if (!InputValidator.IsTag(tag)) + throw new ArgumentException(Constants.INVALID_TAG_INPUT_ERROR); + } + } + + if (approvees != null) + { + foreach (var approve in approvees) + { + if (!InputValidator.IsHash(approve)) + throw new ArgumentException(Constants.INVALID_BUNDLE_HASH_ERROR); + } + } + + if (bundles != null) + { + foreach (var bundle in bundles) + { + if (!InputValidator.IsHash(bundle)) + throw new ArgumentException(Constants.INVALID_BUNDLE_HASH_ERROR); + + } + } + + + var findTransactionsRequest = new FindTransactionsRequest(addresses, tags, approvees, bundles); + return + Request(findTransactionsRequest); + } + + /// + /// Find the transactions by addresses + /// + /// An array of addresses + /// + public FindTransactionsResponse FindTransactionsByAddresses(params string[] addresses) + { + return FindTransactions(addresses.ToList(), null, null, null); + } + + /// + /// + /// + /// + /// + public FindTransactionsResponse FindTransactionsByApprovees(params string[] approvees) + { + return FindTransactions(null, null, approvees.ToList(), null); + } + + /// + /// + /// + /// + /// + public FindTransactionsResponse FindTransactionsByBundles(params string[] bundles) + { + return FindTransactions(null, null, null, bundles.ToList()); + } + + /// + /// + /// + /// + /// + public FindTransactionsResponse FindTransactionsByTags(params string[] tags) + { + return FindTransactions(null, tags.ToList(), null, null); + } + + /// + /// + /// + /// + /// + public GetTrytesResponse GetTrytes(params string[] hashes) + { + var getTrytesRequest = new GetTrytesRequest(hashes); + return Request(getTrytesRequest); + } + + /// + /// + /// + /// + /// + /// + public GetInclusionStatesResponse GetInclusionStates(string[] transactions, string[] milestones) + { + return + Request( + new GetInclusionStatesRequest(transactions, milestones)); + } + + /// + /// + /// + /// + /// + /// + public GetTransactionsToApproveResponse GetTransactionsToApprove(int depth, string reference = null) + { + var getTransactionsToApproveRequest = + new GetTransactionsToApproveRequest(depth, reference); + return + Request( + getTransactionsToApproveRequest); + } + + /// + /// + /// + /// The confirmation threshold, should be set to 100. + /// + /// + /// + public GetBalancesResponse GetBalances(long threshold, List addresses, List tips) + { + List addressesWithoutChecksum = new List(); + foreach (var address in addresses) + { + addressesWithoutChecksum.Add(address.RemoveChecksum()); + } + + GetBalancesRequest getBalancesRequest = new GetBalancesRequest(threshold, addressesWithoutChecksum, tips); + return Request(getBalancesRequest); + } + + /// + /// + /// + /// + /// + public WereAddressesSpentFromResponse WereAddressesSpentFrom(params string[] addresses) + { + List addressesWithoutChecksum = new List(); + foreach (var address in addresses) + { + addressesWithoutChecksum.Add(address.RemoveChecksum()); + } + + WereAddressesSpentFromRequest wereAddressesSpentFromRequest = + new WereAddressesSpentFromRequest(addressesWithoutChecksum); + return Request( + wereAddressesSpentFromRequest); + } + + /// + /// + /// + /// + /// + public CheckConsistencyResponse CheckConsistency(params string[] tails) + { + if (!InputValidator.IsArrayOfHashes(tails)) + throw new ArgumentException("Invalid hashes provided."); + + var checkConsistencyRequest = new CheckConsistencyRequest(tails); + return Request(checkConsistencyRequest); + } + + /// + /// + /// + /// + public InterruptAttachingToTangleResponse InterruptAttachingToTangle() + { + return + Request( + new InterruptAttachingToTangleRequest()); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public AttachToTangleResponse AttachToTangle( + string trunkTransaction, string branchTransaction, + int minWeightMagnitude, string[] trytes) + { + if (!InputValidator.IsHash(trunkTransaction)) + throw new ArgumentException(Constants.INVALID_HASH_INPUT_ERROR); + + if (!InputValidator.IsHash(branchTransaction)) + throw new ArgumentException(Constants.INVALID_HASH_INPUT_ERROR); + + if (!InputValidator.IsArrayOfRawTransactionTrytes(trytes)) + throw new ArgumentException(Constants.INVALID_TRYTES_INPUT_ERROR); + + var attachToTangleRequest = + new AttachToTangleRequest( + trunkTransaction, branchTransaction, minWeightMagnitude, trytes); + return Request(attachToTangleRequest); + } + + /// + /// + /// + /// + /// + public BroadcastTransactionsResponse BroadcastTransactions(string[] trytes) + { + if (!InputValidator.IsArrayOfRawTransactionTrytes(trytes)) + throw new ArgumentException(Constants.INVALID_ATTACHED_TRYTES_INPUT_ERROR); + + return + Request( + new BroadcastTransactionsRequest(trytes)); + } + + /// + /// + /// + /// + /// + public StoreTransactionsResponse StoreTransactions(string[] trytes) + { + if (!InputValidator.IsArrayOfRawTransactionTrytes(trytes)) + throw new ArgumentException(Constants.INVALID_ATTACHED_TRYTES_INPUT_ERROR); + + return + Request( + new StoreTransactionsRequest(trytes)); + } + + /// + /// + /// + /// + public GetNeighborsResponse GetNeighbors() + { + return Request(new GetNeighborsRequest()); + } + + /// + /// + /// + /// + /// + public AddNeighborsResponse AddNeighbors(params string[] uris) + { + return + Request( + new AddNeighborsRequest(uris)); + } + + /// + /// + /// + /// + /// + public RemoveNeighborsResponse RemoveNeighbors(params string[] uris) + { + return + Request( + new RemoveNeighborsRequest(uris)); + } + + private TResponse Request(TRequest request) where TResponse : new() + { + return _jsonWebClient.GetPOSTResponseSync( + new Uri(UriString), + JsonConvert.SerializeObject(request, _jsonSerializerSettings)); + } + } +} diff --git a/Src/IotaSharp/IotaSharp.csproj b/Src/IotaSharp/IotaSharp.csproj new file mode 100644 index 0000000..08de1a4 --- /dev/null +++ b/Src/IotaSharp/IotaSharp.csproj @@ -0,0 +1,44 @@ + + + + net461;netstandard2.0 + $(LibraryFrameworks) + latest + 1.0.0.1 + 1.0.0.0 + 1.0.1 + IotaSharp + Iota + IotaSharp + Iota Library for .NET + Copyright © Iota 2008 + Iota Library for .NET + en-US + IotaSharp + IotaSharp + Iota + MIT + IotaSharp + IotaSharp + true + 2.12 + true + snupkg + IotaSharp.ruleset + false + + + + + + + + + + + IotaSharp + + + IotaSharp .NET Standard 2.0 + + \ No newline at end of file diff --git a/Src/IotaSharp/IotaSharp.ruleset b/Src/IotaSharp/IotaSharp.ruleset new file mode 100644 index 0000000..a49f1e2 --- /dev/null +++ b/Src/IotaSharp/IotaSharp.ruleset @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IotaCSharpApi/Api/Model/Bundle.cs b/Src/IotaSharp/Model/Bundle.cs similarity index 71% rename from IotaCSharpApi/Api/Model/Bundle.cs rename to Src/IotaSharp/Model/Bundle.cs index ea0302f..6e8bc29 100644 --- a/IotaCSharpApi/Api/Model/Bundle.cs +++ b/Src/IotaSharp/Model/Bundle.cs @@ -1,223 +1,250 @@ -using System; -using System.Collections.Generic; -using Iota.Lib.CSharp.Api.Pow; -using Iota.Lib.CSharp.Api.Utils; - -namespace Iota.Lib.CSharp.Api.Model -{ - /// - /// This class represents a Bundle, a set of transactions - /// - public class Bundle : IComparable - { - /// - /// Initializes a new instance of the class without transactions. - /// - public Bundle() : this(new List(), 0) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The transactions. - /// The length. - public Bundle(List transactions, int length) - { - Transactions = transactions; - Length = length; - } - - /// - /// Gets the at the specified index. - /// - /// - /// The . - /// - /// The index. - /// - public Transaction this[int index] => Transactions[index]; - - /// - /// Gets or sets the transactions. - /// - /// - /// The transactions. - /// - public List Transactions { get; set; } - - /// - /// Gets or sets the length of the bundle - /// - /// - /// The length. - /// - public int Length { get; set; } - - /// - /// Compares the current object with another object of the same type. - /// - /// An object to compare with this object. - /// - /// A value that indicates the relative order of the objects being compared. The return value has the following - /// meanings: Value Meaning Less than zero This object is less than the parameter.Zero This - /// object is equal to . Greater than zero This object is greater than - /// . - /// - public int CompareTo(Bundle other) - { - var timeStamp1 = Transactions[0].Timestamp; - var timeStamp2 = other.Transactions[0].Timestamp; - - if (timeStamp1 < timeStamp2) - return -1; - if (timeStamp1 > timeStamp2) - return 1; - return 0; - } - - /// - /// Adds a bundle entry - /// - /// Length of the signature message. - /// The address. - /// The value. - /// The tag. - /// The timestamp. - public void AddEntry(int signatureMessageLength, string address, long value, string tag, long timestamp) - { - for (var i = 0; i < signatureMessageLength; i++) - { - var trx = new Transaction(address, i == 0 ? value : 0, tag, timestamp); - Transactions.Add(trx); - } - } - - /// - /// Adds the trytes. - /// - /// The signature fragments. - public void AddTrytes(List signatureFragments) - { - var emptySignatureFragment = ""; - var emptyHash = Constants.EmptyHash; - long emptyTimestamp = 999999999L; - - while (emptySignatureFragment.Length < 2187) emptySignatureFragment += '9'; - - for (var i = 0; i < Transactions.Count; i++) - { - var transaction = Transactions[i]; - - // Fill empty signatureMessageFragment - transaction.SignatureMessageFragment = signatureFragments.Count <= i || - string.IsNullOrEmpty(signatureFragments[i]) - ? emptySignatureFragment - : signatureFragments[i]; - // Fill empty trunkTransaction - transaction.TrunkTransaction = emptyHash; - - // Fill empty branchTransaction - transaction.BranchTransaction = emptyHash; - - transaction.AttachmentTimestamp = emptyTimestamp; - transaction.AttachmentTimestampLowerBound = emptyTimestamp; - transaction.AttachmentTimestampUpperBound = emptyTimestamp; - // Fill empty nonce - transaction.Nonce = "999999999999999999999999999"; - } - } - - - /// - /// Normalizeds the bundle. - /// - /// The bundle hash. - /// - public int[] NormalizedBundle(string bundleHash) - { - var normalizedBundle = new int[81]; - - for (var i = 0; i < 3; i++) - { - long sum = 0; - for (var j = 0; j < 27; j++) - sum += - normalizedBundle[i * 27 + j] = - Converter.ToValue(Converter.ToTrits("" + bundleHash[i * 27 + j])); - - if (sum >= 0) - { - while (sum-- > 0) - for (var j = 0; j < 27; j++) - if (normalizedBundle[i * 27 + j] > -13) - { - normalizedBundle[i * 27 + j]--; - break; - } - } - else - { - while (sum++ < 0) - for (var j = 0; j < 27; j++) - if (normalizedBundle[i * 27 + j] < 13) - { - normalizedBundle[i * 27 + j]++; - break; - } - } - } - - return normalizedBundle; - } - - - /// - /// Finalizes the bundle using the specified curl implementation - /// - /// The custom curl. - public void FinalizeBundle(ICurl customCurl) - { - customCurl.Reset(); - - for (var i = 0; i < Transactions.Count; i++) - { - var valueTrits = Converter.ToTrits(Transactions[i].Value, 81); - - var timestampTrits = Converter.ToTrits(Transactions[i].Timestamp, 27); - - var currentIndexTrits = Converter.ToTrits(Transactions[i].CurrentIndex = i, 27); - - var lastIndexTrits = Converter.ToTrits( - Transactions[i].LastIndex = Transactions.Count - 1, 27); - - var stringToConvert = Transactions[i].Address - + Converter.ToTrytes(valueTrits) - + Transactions[i].Tag + - Converter.ToTrytes(timestampTrits) - + Converter.ToTrytes(currentIndexTrits) + - Converter.ToTrytes(lastIndexTrits); - - var t = Converter.ToTrits(stringToConvert); - customCurl.Absorb(t, 0, t.Length); - } - - var hash = new int[243]; - customCurl.Squeeze(hash, 0, hash.Length); - var hashInTrytes = Converter.ToTrytes(hash); - - foreach (var transaction in Transactions) transaction.Bundle = hashInTrytes; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"{nameof(Transactions)}: {string.Join(",", Transactions)}"; - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using IotaSharp.Pow; +using IotaSharp.Utils; + +namespace IotaSharp.Model +{ + /// + /// This class represents a Bundle, a set of transactions + /// + public class Bundle : IComparable + { + /// + /// Initializes a new instance of the class without transactions. + /// + public Bundle() : this(new List(), 0) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transactions. + /// The length. + public Bundle(List transactions, int length) + { + Transactions = transactions; + Length = length; + } + + /// + /// Gets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// + public Transaction this[int index] => Transactions[index]; + + /// + /// Gets or sets the transactions. + /// + /// + /// The transactions. + /// + public List Transactions { get; private set; } + + /// + /// Gets or sets the length of the bundle + /// + /// + /// The length. + /// + public int Length { get; set; } + + /// + /// Compares the current object with another object of the same type. + /// + /// An object to compare with this object. + /// + /// A value that indicates the relative order of the objects being compared. The return value has the following + /// meanings: Value Meaning Less than zero This object is less than the parameter.Zero This + /// object is equal to . Greater than zero This object is greater than + /// . + /// + public int CompareTo(Bundle other) + { + var timeStamp1 = Transactions[0].Timestamp; + var timeStamp2 = other.Transactions[0].Timestamp; + + if (timeStamp1 < timeStamp2) + return -1; + if (timeStamp1 > timeStamp2) + return 1; + return 0; + } + + /// + /// Adds a bundle entry + /// + /// Length of the signature message. + /// The address. + /// The value. + /// The tag. + /// The timestamp. + public void AddEntry(int signatureMessageLength, string address, long value, string tag, long timestamp) + { + if (Transactions == null) + Transactions = new List(); + + for (var i = 0; i < signatureMessageLength; i++) + { + var trx = new Transaction(address, i == 0 ? value : 0, tag, timestamp); + Transactions.Add(trx); + } + } + + /// + /// Adds the trytes. + /// + /// The signature fragments. + public void AddTrytes(List signatureFragments) + { + var emptySignatureFragment = ""; + var emptyHash = Constants.NULL_HASH; + long emptyTimestamp = 999999999L; + + while (emptySignatureFragment.Length < 2187) emptySignatureFragment += '9'; + + for (var i = 0; i < Transactions.Count; i++) + { + var transaction = Transactions[i]; + + // Fill empty signatureMessageFragment + transaction.SignatureMessageFragment = signatureFragments.Count <= i || + string.IsNullOrEmpty(signatureFragments[i]) + ? emptySignatureFragment + : signatureFragments[i]; + // Fill empty trunkTransaction + transaction.TrunkTransaction = emptyHash; + + // Fill empty branchTransaction + transaction.BranchTransaction = emptyHash; + + transaction.AttachmentTimestamp = emptyTimestamp; + transaction.AttachmentTimestampLowerBound = emptyTimestamp; + transaction.AttachmentTimestampUpperBound = emptyTimestamp; + // Fill empty nonce + transaction.Nonce = "999999999999999999999999999"; + } + } + + + /// + /// Normalizeds the bundle. + /// + /// The bundle hash. + /// + public sbyte[] NormalizedBundle(string bundleHash) + { + var normalizedBundle = new sbyte[81]; + + for (var i = 0; i < 3; i++) + { + long sum = 0; + for (var j = 0; j < 27; j++) + sum += + normalizedBundle[i * 27 + j] = + (sbyte) Converter.ToInt32(Converter.ToTrits("" + bundleHash[i * 27 + j])); + + if (sum >= 0) + { + while (sum-- > 0) + for (var j = 0; j < 27; j++) + if (normalizedBundle[i * 27 + j] > -13) + { + normalizedBundle[i * 27 + j]--; + break; + } + } + else + { + while (sum++ < 0) + for (var j = 0; j < 27; j++) + if (normalizedBundle[i * 27 + j] < 13) + { + normalizedBundle[i * 27 + j]++; + break; + } + } + } + + return normalizedBundle; + } + + + /// + /// Finalizes the bundle using the specified curl implementation + /// + /// The custom curl. + public void FinalizeBundle(ICurl customCurl) + { + string hashInTrytes = string.Empty; + + bool validBundle = false; + + while (!validBundle) + { + customCurl.Reset(); + + for (var i = 0; i < Transactions.Count; i++) + { + var valueTrits = Converter.ToTrits(Transactions[i].Value, 81); + + var timestampTrits = Converter.ToTrits(Transactions[i].Timestamp, 27); + + var currentIndexTrits = Converter.ToTrits(Transactions[i].CurrentIndex = i, 27); + + var lastIndexTrits = Converter.ToTrits( + Transactions[i].LastIndex = Transactions.Count - 1, 27); + + var stringToConvert = Transactions[i].Address + + Converter.ToTrytes(valueTrits) + + Transactions[i].ObsoleteTag + + Converter.ToTrytes(timestampTrits) + + Converter.ToTrytes(currentIndexTrits) + + Converter.ToTrytes(lastIndexTrits); + + var t = Converter.ToTrits(stringToConvert); + customCurl.Absorb(t, 0, t.Length); + } + + var hash = new sbyte[Sponge.HASH_LENGTH]; + customCurl.Squeeze(hash, 0, hash.Length); + hashInTrytes = Converter.ToTrytes(hash); + + bool foundValue = false; + var normalizedHash = NormalizedBundle(hashInTrytes); + foreach (var normalizedHashValue in normalizedHash) + { + if (normalizedHashValue == 13 /* = M */) + { + foundValue = true; + // Insecure bundle. Increment Tag and recompute bundle hash. + var obsoleteTagTrits = Converter.ToTrits(Transactions[0].ObsoleteTag); + Converter.Increment(obsoleteTagTrits, 81); + Transactions[0].ObsoleteTag = Converter.ToTrytes(obsoleteTagTrits); + break; + } + } + + validBundle = !foundValue; + } + + foreach (var transaction in Transactions) transaction.Bundle = hashInTrytes; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return $"{nameof(Transactions)}: {string.Join(",", Transactions)}"; + } + } +} diff --git a/IotaApi.Standard/Model/Input.cs b/Src/IotaSharp/Model/Input.cs similarity index 86% rename from IotaApi.Standard/Model/Input.cs rename to Src/IotaSharp/Model/Input.cs index 4ed10bc..cd7afd8 100644 --- a/IotaApi.Standard/Model/Input.cs +++ b/Src/IotaSharp/Model/Input.cs @@ -1,4 +1,4 @@ -namespace Iota.Api.Standard.Model +namespace IotaSharp.Model { /// /// This class represents an Input @@ -36,6 +36,7 @@ public class Input /// The security. /// public int Security { get; set; } + /// /// Returns a that represents this instance. /// @@ -44,7 +45,8 @@ public class Input /// public override string ToString() { - return $"{nameof(Address)}: {Address}, {nameof(Balance)}: {Balance}, {nameof(KeyIndex)}: {KeyIndex}, {nameof(Security)}: {Security}"; + return + $"{nameof(Address)}: {Address}, {nameof(Balance)}: {Balance}, {nameof(KeyIndex)}: {KeyIndex}, {nameof(Security)}: {Security}"; } } -} \ No newline at end of file +} diff --git a/IotaApi.Standard/Model/Neighbor.cs b/Src/IotaSharp/Model/Neighbor.cs similarity index 80% rename from IotaApi.Standard/Model/Neighbor.cs rename to Src/IotaSharp/Model/Neighbor.cs index f9f4cef..c9f6b34 100644 --- a/IotaApi.Standard/Model/Neighbor.cs +++ b/Src/IotaSharp/Model/Neighbor.cs @@ -1,7 +1,7 @@ -namespace Iota.Api.Standard.Model +namespace IotaSharp.Model { /// - /// This class represents a neigbhor + /// /// public class Neighbor { @@ -45,7 +45,8 @@ public class Neighbor /// public override string ToString() { - return $"{nameof(Address)}: {Address}, {nameof(NumberOfAllTransactions)}: {NumberOfAllTransactions}, {nameof(NumberOfInvalidTransactions)}: {NumberOfInvalidTransactions}, {nameof(NumberOfNewTransactions)}: {NumberOfNewTransactions}"; + return + $"{nameof(Address)}: {Address}, {nameof(NumberOfAllTransactions)}: {NumberOfAllTransactions}, {nameof(NumberOfInvalidTransactions)}: {NumberOfInvalidTransactions}, {nameof(NumberOfNewTransactions)}: {NumberOfNewTransactions}"; } } -} \ No newline at end of file +} diff --git a/IotaApi.Standard/Model/Transaction.cs b/Src/IotaSharp/Model/Transaction.cs similarity index 66% rename from IotaApi.Standard/Model/Transaction.cs rename to Src/IotaSharp/Model/Transaction.cs index 56179a2..e1e3876 100644 --- a/IotaApi.Standard/Model/Transaction.cs +++ b/Src/IotaSharp/Model/Transaction.cs @@ -1,72 +1,21 @@ using System; -using Iota.Api.Standard.Pow; -using Iota.Api.Standard.Utils; +using IotaSharp.Pow; +using IotaSharp.Utils; -namespace Iota.Api.Standard.Model +namespace IotaSharp.Model { /// /// This class represents an iota transaction /// public class Transaction { - /// - /// Initializes a new instance of the class. - /// - public Transaction() - { - } - /// /// Initializes a new instance of the class. /// /// The trytes representing the transaction - /// The curl implementation. - /// - /// trytes must non-null - /// or - /// position " + i + "must not be '9' - /// - public Transaction(string trytes, ICurl curl) - { - if (string.IsNullOrEmpty(trytes)) throw new ArgumentException("trytes must non-null"); - - // validity check - for (var i = 2279; i < 2295; i++) - if (trytes[i] != '9') - throw new ArgumentException("position " + i + "must not be '9'"); - - var transactionTrits = Converter.ToTrits(trytes); - var hash = new int[243]; - - // generate the correct transaction hash - curl.Reset(); - curl.Absorb(transactionTrits, 0, transactionTrits.Length); - curl.Squeeze(hash, 0, hash.Length); - - Hash = Converter.ToTrytes(hash); - SignatureMessageFragment = trytes.Substring(0, 2187); - Address = trytes.Substring(2187, 2268 - 2187); - Value = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6804, 6837)); - ObsoleteTag = trytes.Substring(2295, 2322 - 2295); - Timestamp = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6966, 6993)); - CurrentIndex = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6993, 7020)); - LastIndex = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7020, 7047)); - Bundle = trytes.Substring(2349, 2430 - 2349); - TrunkTransaction = trytes.Substring(2430, 2511 - 2430); - BranchTransaction = trytes.Substring(2511, 2592 - 2511); - Tag = trytes.Substring(2592, 2619 - 2592); - AttachmentTimestamp = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7857, 7884)); - AttachmentTimestampLowerBound = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7884, 7911)); - AttachmentTimestampUpperBound = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7911, 7938)); - Nonce = trytes.Substring(2646, 2673 - 2646); - } - - /// - /// Initializes a new instance of the class. - /// - /// The trytes representing the transaction - public Transaction(string trytes) : this(trytes, new Curl(CurlMode.CurlP81)) + public Transaction(string trytes) { + TransactionObject(trytes); } /// @@ -85,14 +34,6 @@ public Transaction(string address, long value, string tag, long timestamp) Timestamp = timestamp; } - /// - /// Gets or sets the hash. - /// - /// - /// The hash. - /// - public string Hash { get; set; } - /// /// Gets or sets the signature fragment. /// @@ -101,13 +42,20 @@ public Transaction(string address, long value, string tag, long timestamp) /// public string SignatureMessageFragment { get; set; } + + private string _address; + /// /// Gets or sets the address. /// /// /// The address. /// - public string Address { get; set; } + public string Address + { + get => _address; + set => _address = value.RemoveChecksum(); + } /// /// Gets or sets the value. @@ -197,20 +145,13 @@ public Transaction(string address, long value, string tag, long timestamp) /// public string Nonce { get; set; } - - /// - /// Gets or sets a value indicating whether this is persistance. - /// - /// - /// true if persistance; otherwise, false. - /// - public bool Persistance { get; set; } + public bool Persistence { get; set; } /// /// Converts the transaction to the corresponding trytes representation /// /// - public string ToTransactionTrytes() + public string ToTrytes() { var valueTrits = Converter.ToTrits(Value, 81); var timestampTrits = Converter.ToTrits(Timestamp, 27); @@ -241,27 +182,48 @@ public string ToTransactionTrytes() } /// - /// Returns a that represents this instance. + /// /// - /// - /// A that represents this instance. - /// - public override string ToString() + /// + public string CurlHash() + { + var transactionTrits = Converter.ToTrits(ToTrytes()); + var hash = new sbyte[Sponge.HASH_LENGTH]; + + ICurl curl = SpongeFactory.Create(SpongeFactory.Mode.CURLP81); + curl.Reset(); + curl.Absorb(transactionTrits); + curl.Squeeze(hash); + + return Converter.ToTrytes(hash); + } + + private void TransactionObject(string trytes) { - return - $@"{nameof(Value)}: {Value}, -{nameof(Persistance)}: {Value}, -{nameof(Tag)}: {Tag}, -{nameof(Hash)}: {Hash}, -{nameof(SignatureMessageFragment)}: {SignatureMessageFragment}, -{nameof(Address)}: {Address}, -{nameof(Timestamp)}: {Timestamp}, -{nameof(Bundle)}: {Bundle}, -{nameof(TrunkTransaction)}: {TrunkTransaction}, -{nameof(BranchTransaction)}: {BranchTransaction}, -{nameof(LastIndex)}: {LastIndex}, -{nameof(CurrentIndex)}: {CurrentIndex}, -{nameof(Nonce)}: {Nonce}"; + if (string.IsNullOrEmpty(trytes)) throw new ArgumentException("trytes must non-null"); + + // validity check + for (var i = 2279; i < 2295; i++) + if (trytes[i] != '9') + throw new ArgumentException("position " + i + "must not be '9'"); + + var transactionTrits = Converter.ToTrits(trytes); + + SignatureMessageFragment = trytes.Substring(0, 2187); + Address = trytes.Substring(2187, 2268 - 2187); + Value = Converter.ToInt64(transactionTrits, 6804, 6837 - 6804); + ObsoleteTag = trytes.Substring(2295, 2322 - 2295); + Timestamp = Converter.ToInt64(transactionTrits, 6966, 6993 - 6966); + CurrentIndex = Converter.ToInt64(transactionTrits, 6993, 7020 - 6993); + LastIndex = Converter.ToInt64(transactionTrits, 7020, 7047 - 7020); + Bundle = trytes.Substring(2349, 2430 - 2349); + TrunkTransaction = trytes.Substring(2430, 2511 - 2430); + BranchTransaction = trytes.Substring(2511, 2592 - 2511); + Tag = trytes.Substring(2592, 2619 - 2592); + AttachmentTimestamp = Converter.ToInt64(transactionTrits, 7857, 7884 - 7857); + AttachmentTimestampLowerBound = Converter.ToInt64(transactionTrits, 7884, 7911 - 7884); + AttachmentTimestampUpperBound = Converter.ToInt64(transactionTrits, 7911, 7938 - 7911); + Nonce = trytes.Substring(2646, 2673 - 2646); } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Model/Transfer.cs b/Src/IotaSharp/Model/Transfer.cs new file mode 100644 index 0000000..fd02e7a --- /dev/null +++ b/Src/IotaSharp/Model/Transfer.cs @@ -0,0 +1,82 @@ +namespace IotaSharp.Model +{ + /// + /// This class represents a Transfer. + /// + public class Transfer + { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Transfer( + string timestamp, string address, string hash, + bool persistence, + long value, string message, string tag) + { + Timestamp = timestamp; + Address = address; + Hash = hash; + Persistence = persistence; + Value = value; + Message = message; + Tag = tag; + } + + /// + /// + /// + /// must contain checksum + /// + /// + /// + public Transfer(string address, long value, string message = "", string tag = "") + { + Address = address; + Value = value; + Message = message; + Tag = tag; + } + + /// + /// Timestamp + /// + public string Timestamp { get; set; } + + /// + /// Address, must contain checksum + /// + public string Address { get; set; } + + /// + /// Hash + /// + public string Hash { get; set; } + + /// + /// Persistence + /// + public bool Persistence { get; set; } + + /// + /// Value + /// + public long Value { get; set; } + + /// + /// Message + /// + public string Message { get; set; } + + /// + /// Tag + /// + public string Tag { get; set; } + } +} diff --git a/Src/IotaSharp/Pow/Curl.cs b/Src/IotaSharp/Pow/Curl.cs new file mode 100644 index 0000000..96882d5 --- /dev/null +++ b/Src/IotaSharp/Pow/Curl.cs @@ -0,0 +1,131 @@ +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; + +namespace IotaSharp.Pow +{ + /// + /// + /// + [SuppressMessage("ReSharper", "IdentifierTypo")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class Curl : Sponge + { + private const int STATE_LENGTH = 3 * HASH_LENGTH; + private const int NUMBER_OF_ROUNDSP81 = 81; + private const int NUMBER_OF_ROUNDSP27 = 27; + + private static readonly sbyte[] TruthTable = {1, 0, -1, 2, 1, -1, 0, 2, -1, 1, 0}; + private readonly int _numberOfRounds; + private readonly sbyte[] _scratchpad = new sbyte[STATE_LENGTH]; + + /// + /// + /// + /// + public Curl(SpongeFactory.Mode mode) + { + switch (mode) + { + case SpongeFactory.Mode.CURLP81: + _numberOfRounds = NUMBER_OF_ROUNDSP81; + break; + case SpongeFactory.Mode.CURLP27: + _numberOfRounds = NUMBER_OF_ROUNDSP27; + break; + default: + throw new InvalidEnumArgumentException("Only Curl-P-27 and Curl-P-81 are supported."); + + } + + State = new sbyte[STATE_LENGTH]; + } + + /// + /// + /// + /// + /// + /// + /// + public override ICurl Absorb(sbyte[] trits, int offset, int length) + { + do + { + Array.Copy(trits, offset, State, 0, length < HASH_LENGTH ? length : HASH_LENGTH); + Transform(); + offset += HASH_LENGTH; + } while ((length -= HASH_LENGTH) > 0); + + return this; + } + + /// + /// + /// + /// + /// + /// + /// + public override sbyte[] Squeeze(sbyte[] trits, int offset, int length) + { + do + { + Array.Copy(State, 0, trits, offset, length < HASH_LENGTH ? length : HASH_LENGTH); + Transform(); + offset += HASH_LENGTH; + } while ((length -= HASH_LENGTH) > 0); + + return trits; + } + + /// + /// + /// + /// + public override ICurl Reset() + { + Array.Clear(State, 0, State.Length); + return this; + } + + /// + /// + /// + /// + public override ICurl Clone() + { + if (_numberOfRounds == NUMBER_OF_ROUNDSP27) + return new Curl(SpongeFactory.Mode.CURLP27); + + if (_numberOfRounds == NUMBER_OF_ROUNDSP81) + return new Curl(SpongeFactory.Mode.CURLP81); + + return null; + } + + private void Transform() + { + var scratchpadIndex = 0; + + for (var round = 0; round < _numberOfRounds; round++) + { + Array.Copy(State, 0, _scratchpad, 0, STATE_LENGTH); + for (var stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) + { + var prevScratchpadIndex = scratchpadIndex; + if (scratchpadIndex < 365) + scratchpadIndex += 364; + else + scratchpadIndex += -365; + + State[stateIndex] = + TruthTable[ + _scratchpad[prevScratchpadIndex] + + (_scratchpad[scratchpadIndex] << 2) + + 5]; + } + } + } + } +} diff --git a/IotaApi.Standard/Pow/Sponge.cs b/Src/IotaSharp/Pow/ICurl.cs similarity index 64% rename from IotaApi.Standard/Pow/Sponge.cs rename to Src/IotaSharp/Pow/ICurl.cs index c85dfbd..fc177c6 100644 --- a/IotaApi.Standard/Pow/Sponge.cs +++ b/Src/IotaSharp/Pow/ICurl.cs @@ -1,29 +1,25 @@ -namespace Iota.Api.Standard.Pow +namespace IotaSharp.Pow { /// /// /// - public abstract class Sponge : ICurl + public interface ICurl { - - /// - /// - /// - public const int HashLength = 243; - /// /// /// /// /// /// - public abstract void Absorb(int[] trits, int offset, int length); - + /// + ICurl Absorb(sbyte[] trits, int offset, int length); + /// /// /// /// - public void Absorb(int[] trits) => Absorb(trits,0,trits.Length); + /// + ICurl Absorb(sbyte[] trits); /// /// @@ -32,25 +28,35 @@ public abstract class Sponge : ICurl /// /// /// - public abstract void Squeeze(int[] trits, int offset, int length); - + sbyte[] Squeeze(sbyte[] trits, int offset, int length); + /// /// /// /// /// - public void Squeeze(int[] trits) => Squeeze(trits,0,trits.Length); + sbyte[] Squeeze(sbyte[] trits); /// /// /// /// - public abstract void Reset(); + ICurl Reset(); /// /// /// /// - public abstract ICurl Clone(); + ICurl Clone(); + + /// + /// + /// + sbyte[] State { get; set; } + + /// + /// HashLength of state + /// + sbyte[] Rate { get; } } } diff --git a/IotaApi.Standard/Pow/Kerl.cs b/Src/IotaSharp/Pow/Kerl.cs similarity index 53% rename from IotaApi.Standard/Pow/Kerl.cs rename to Src/IotaSharp/Pow/Kerl.cs index a27b312..383d49e 100644 --- a/IotaApi.Standard/Pow/Kerl.cs +++ b/Src/IotaSharp/Pow/Kerl.cs @@ -1,89 +1,71 @@ using System; -using Iota.Api.Standard.Exception; -using Iota.Api.Standard.Utils; +using System.Diagnostics.CodeAnalysis; +using IotaSharp.Exception; +using IotaSharp.Utils; using Org.BouncyCastle.Crypto.Digests; -namespace Iota.Api.Standard.Pow +namespace IotaSharp.Pow { + /// /// /// + [SuppressMessage("ReSharper", "InconsistentNaming")] public class Kerl : Sponge { - private const int BitHashLength = 384; - private const int ByteHashLength = BitHashLength / 8; + private const int BIT_HASH_LENGTH = 384; + private const int BYTE_HASH_LENGTH = BIT_HASH_LENGTH / 8; + private readonly KeccakDigest _keccak; private readonly byte[] _byteState; - private readonly KeccakDigest _keccak; - private readonly int[] _tritState; + private readonly sbyte[] _tritState; - /// /// + /// /// public Kerl() { - _keccak = new KeccakDigest(BitHashLength); - _byteState = new byte[ByteHashLength]; - _tritState = new int[HashLength]; - } - - /// - /// - /// - /// - public override void Reset() - { - _keccak.Reset(); + _keccak = new KeccakDigest(BIT_HASH_LENGTH); + _byteState = new byte[BYTE_HASH_LENGTH]; + _tritState = new sbyte[HASH_LENGTH]; } /// /// /// - /// - public override ICurl Clone() - { - return new Kerl(); - } - - /// - /// - /// /// /// /// /// - public override void Absorb(int[] trits, int offset, int length) + public override ICurl Absorb(sbyte[] trits, int offset, int length) { if (length % 243 != 0) throw new IllegalStateException("Illegal length: " + length); do { - //copy trits[offset:offset+length] - Array.Copy(trits, offset, _tritState, 0, HashLength); + Array.Copy(trits, offset, _tritState, 0, HASH_LENGTH); //convert to bits - _tritState[HashLength - 1] = 0; + _tritState[HASH_LENGTH - 1] = 0; FixedBigIntConverter.FromTritsToBytes(_tritState, _byteState); - //BigIntConverter.BytesFromBigInt( - // BigIntConverter.BigIntFromTrits(_tritState, 0, HashLength), - // _byteState, 0, ByteHashLength); - + //run keccak _keccak.BlockUpdate(_byteState, 0, _byteState.Length); - offset += HashLength; - } while ((length -= HashLength) > 0); + offset += HASH_LENGTH; + } while ((length -= HASH_LENGTH) > 0); + + return this; } - /// /// + /// /// /// /// /// /// - /// - public override void Squeeze(int[] trits, int offset, int length) + public override sbyte[] Squeeze(sbyte[] trits, int offset, int length) { if (length % 243 != 0) throw new IllegalStateException("Illegal length: " + length); @@ -93,20 +75,38 @@ public override void Squeeze(int[] trits, int offset, int length) //convert to trits FixedBigIntConverter.FromBytesToTrits(_byteState, _tritState); - //BigIntConverter.TritsFromBigInt( - // BigIntConverter.BigIntFromBytes(_byteState, 0, ByteHashLength), - // _tritState, 0, HashLength); //copy with offset - _tritState[HashLength - 1] = 0; - Array.Copy(_tritState, 0, trits, offset, HashLength); + _tritState[HASH_LENGTH - 1] = 0; + Array.Copy(_tritState, 0, trits, offset, HASH_LENGTH); //calculate hash again - for (var i = _byteState.Length; i-- > 0;) _byteState[i] = (byte) (_byteState[i] ^ 0xFF); + for (var i = _byteState.Length; i-- > 0;) _byteState[i] = (byte)(_byteState[i] ^ 0xFF); _keccak.BlockUpdate(_byteState, 0, _byteState.Length); - offset += HashLength; - } while ((length -= HashLength) > 0); + offset += HASH_LENGTH; + } while ((length -= HASH_LENGTH) > 0); + + return trits; + } + + /// + /// + /// + /// + public override ICurl Reset() + { + _keccak.Reset(); + return this; + } + + /// + /// + /// + /// + public override ICurl Clone() + { + return new Kerl(); } } -} \ No newline at end of file +} diff --git a/IotaCSharpApi/Api/Pow/PearlDiver.cs b/Src/IotaSharp/Pow/PearlDiver.cs similarity index 77% rename from IotaCSharpApi/Api/Pow/PearlDiver.cs rename to Src/IotaSharp/Pow/PearlDiver.cs index 878b035..9f84f78 100644 --- a/IotaCSharpApi/Api/Pow/PearlDiver.cs +++ b/Src/IotaSharp/Pow/PearlDiver.cs @@ -1,19 +1,22 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -namespace Iota.Lib.CSharp.Api.Pow +namespace IotaSharp.Pow { /// /// (c) 2016 Come-from-Beyond /// See https://github.com/iotaledger/iri/blob/dev/src/main/java/com/iota/iri/hash/PearlDiver.java /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "IdentifierTypo")] public class PearlDiver { - private const int TransactionLength = 8019; - private const int NumberOfRoundsp81 = 81; + private const int TRANSACTION_LENGTH = 8019; + private const int NUMBER_OF_ROUNDSP81 = 81; - private const int CurlHashLength = 243; - private const int CurlStateLength = CurlHashLength * 3; + private const int CURL_HASH_LENGTH = 243; + private const int CURL_STATE_LENGTH = CURL_HASH_LENGTH * 3; private const long HighBits = -1; private const long LowBits = 0; @@ -28,12 +31,12 @@ public class PearlDiver /// /// // ReSharper disable once RedundantAssignment - public bool Search(int[] transactionTrits, int minWeightMagnitude, int numberOfThreads) + public bool Search(sbyte[] transactionTrits, int minWeightMagnitude, int numberOfThreads) { - if (transactionTrits.Length != TransactionLength) + if (transactionTrits.Length != TRANSACTION_LENGTH) throw new ArgumentException($"Invalid transaction trits length: {transactionTrits.Length}"); - if (minWeightMagnitude < 0 || minWeightMagnitude > CurlHashLength) + if (minWeightMagnitude < 0 || minWeightMagnitude > CURL_HASH_LENGTH) throw new ArgumentException($"Invalid min weight magnitude: {minWeightMagnitude}"); lock (SyncObj) @@ -41,24 +44,24 @@ public bool Search(int[] transactionTrits, int minWeightMagnitude, int numberOfT _state = State.Running; } - var midCurlStateLow = new long[CurlStateLength]; - var midCurlStateHigh = new long[CurlStateLength]; + var midCurlStateLow = new long[CURL_STATE_LENGTH]; + var midCurlStateHigh = new long[CURL_STATE_LENGTH]; // step1 { - for (var i = CurlHashLength; i < CurlStateLength; i++) + for (var i = CURL_HASH_LENGTH; i < CURL_STATE_LENGTH; i++) { midCurlStateLow[i] = HighBits; midCurlStateHigh[i] = HighBits; } var offset = 0; - var curlScratchpadLowStep1 = new long[CurlStateLength]; - var curlScratchpadHighStep1 = new long[CurlStateLength]; + var curlScratchpadLowStep1 = new long[CURL_STATE_LENGTH]; + var curlScratchpadHighStep1 = new long[CURL_STATE_LENGTH]; - for (var i = (TransactionLength - CurlHashLength) / CurlHashLength; i-- > 0;) + for (var i = (TRANSACTION_LENGTH - CURL_HASH_LENGTH) / CURL_HASH_LENGTH; i-- > 0;) { - for (var j = 0; j < CurlHashLength; j++) + for (var j = 0; j < CURL_HASH_LENGTH; j++) switch (transactionTrits[offset++]) { case 0: @@ -122,34 +125,34 @@ public bool Search(int[] transactionTrits, int minWeightMagnitude, int numberOfT Parallel.For(0, numberOfThreads, threadIndex => { - var midCurlStateCopyLow = new long[CurlStateLength]; - var midCurlStateCopyHigh = new long[CurlStateLength]; - Array.Copy(midCurlStateLow, 0, midCurlStateCopyLow, 0, CurlStateLength); - Array.Copy(midCurlStateHigh, 0, midCurlStateCopyHigh, 0, CurlStateLength); + var midCurlStateCopyLow = new long[CURL_STATE_LENGTH]; + var midCurlStateCopyHigh = new long[CURL_STATE_LENGTH]; + Array.Copy(midCurlStateLow, 0, midCurlStateCopyLow, 0, CURL_STATE_LENGTH); + Array.Copy(midCurlStateHigh, 0, midCurlStateCopyHigh, 0, CURL_STATE_LENGTH); for (var i = threadIndex; i > 0; i--) - Increment(midCurlStateCopyLow, midCurlStateCopyHigh, 162 + CurlHashLength / 9, - 162 + CurlHashLength / 9 * 2); + Increment(midCurlStateCopyLow, midCurlStateCopyHigh, 162 + CURL_HASH_LENGTH / 9, + 162 + CURL_HASH_LENGTH / 9 * 2); - var curlStateLow = new long[CurlStateLength]; - var curlStateHigh = new long[CurlStateLength]; - var curlScratchpadLowStep2 = new long[CurlStateLength]; - var curlScratchpadHighStep2 = new long[CurlStateLength]; + var curlStateLow = new long[CURL_STATE_LENGTH]; + var curlStateHigh = new long[CURL_STATE_LENGTH]; + var curlScratchpadLowStep2 = new long[CURL_STATE_LENGTH]; + var curlScratchpadHighStep2 = new long[CURL_STATE_LENGTH]; long outMask = 1; while (_state == State.Running) { - Increment(midCurlStateCopyLow, midCurlStateCopyHigh, 162 + CurlHashLength / 9 * 2, - CurlHashLength); + Increment(midCurlStateCopyLow, midCurlStateCopyHigh, 162 + CURL_HASH_LENGTH / 9 * 2, + CURL_HASH_LENGTH); - Array.Copy(midCurlStateCopyLow, 0, curlStateLow, 0, CurlStateLength); - Array.Copy(midCurlStateCopyHigh, 0, curlStateHigh, 0, CurlStateLength); + Array.Copy(midCurlStateCopyLow, 0, curlStateLow, 0, CURL_STATE_LENGTH); + Array.Copy(midCurlStateCopyHigh, 0, curlStateHigh, 0, CURL_STATE_LENGTH); Transform(curlStateLow, curlStateHigh, curlScratchpadLowStep2, curlScratchpadHighStep2); var mask = HighBits; for (var i = minWeightMagnitude; i-- > 0;) { - mask &= ~(curlStateLow[CurlHashLength - 1 - i] ^ curlStateHigh[CurlHashLength - 1 - i]); + mask &= ~(curlStateLow[CURL_HASH_LENGTH - 1 - i] ^ curlStateHigh[CURL_HASH_LENGTH - 1 - i]); if (mask == 0) break; } @@ -163,10 +166,10 @@ public bool Search(int[] transactionTrits, int minWeightMagnitude, int numberOfT _state = State.Completed; while ((outMask & mask) == 0) outMask <<= 1; - for (var i = 0; i < CurlHashLength; i++) - transactionTrits[TransactionLength - CurlHashLength + i] = - (midCurlStateCopyLow[i] & outMask) == 0 ? 1 - : (midCurlStateCopyHigh[i] & outMask) == 0 ? -1 : 0; + for (var i = 0; i < CURL_HASH_LENGTH; i++) + transactionTrits[TRANSACTION_LENGTH - CURL_HASH_LENGTH + i] = + (midCurlStateCopyLow[i] & outMask) == 0 ? (sbyte) 1 + : (midCurlStateCopyHigh[i] & outMask) == 0 ? (sbyte) -1 : (sbyte) 0; } } @@ -191,12 +194,12 @@ private static void Transform(long[] curlStateLow, long[] curlStateHigh, long[] curlScratchpadLow, long[] curlScratchpadHigh) { var curlScratchpadIndex = 0; - for (var round = 0; round < NumberOfRoundsp81; round++) + for (var round = 0; round < NUMBER_OF_ROUNDSP81; round++) { - Array.Copy(curlStateLow, 0, curlScratchpadLow, 0, CurlStateLength); - Array.Copy(curlStateHigh, 0, curlScratchpadHigh, 0, CurlStateLength); + Array.Copy(curlStateLow, 0, curlScratchpadLow, 0, CURL_STATE_LENGTH); + Array.Copy(curlStateHigh, 0, curlScratchpadHigh, 0, CURL_STATE_LENGTH); - for (var curlStateIndex = 0; curlStateIndex < CurlStateLength; curlStateIndex++) + for (var curlStateIndex = 0; curlStateIndex < CURL_STATE_LENGTH; curlStateIndex++) { var alpha = curlScratchpadLow[curlScratchpadIndex]; var beta = curlScratchpadHigh[curlScratchpadIndex]; @@ -241,4 +244,4 @@ private enum State Completed } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Pow/PearlDiverLocalPoW.cs b/Src/IotaSharp/Pow/PearlDiverLocalPoW.cs new file mode 100644 index 0000000..24f2f01 --- /dev/null +++ b/Src/IotaSharp/Pow/PearlDiverLocalPoW.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using IotaSharp.Core; +using IotaSharp.Exception; +using IotaSharp.Model; +using IotaSharp.Utils; + +namespace IotaSharp.Pow +{ + /// + /// + /// + public class PearlDiverLocalPoW + { + private readonly PearlDiver _pearlDiver = new PearlDiver(); + + /// + /// + /// + /// + /// + /// + /// + /// + public AttachToTangleResponse AttachToTangle( + string trunkTransaction, string branchTransaction, + int minWeightMagnitude, string[] trytes) + { + var response = new AttachToTangleResponse + { + Trytes = new List() + }; + + string previousTransaction = null; + foreach (var t in trytes) + { + var txn = new Transaction(t) + { + TrunkTransaction = previousTransaction ?? trunkTransaction, + BranchTransaction = previousTransaction == null ? branchTransaction : trunkTransaction + }; + + if (string.IsNullOrEmpty(txn.Tag) || Regex.IsMatch(txn.Tag, "9*")) + txn.Tag = txn.ObsoleteTag; + + txn.AttachmentTimestamp = TimeStamp.Now(); + txn.AttachmentTimestampLowerBound = 0; + txn.AttachmentTimestampUpperBound = 3_812_798_742_493L; + + // POW + var transactionTrits = Converter.ToTrits(txn.ToTrytes()); + if (!_pearlDiver.Search(transactionTrits, minWeightMagnitude, 0)) + throw new IllegalStateException("PearlDiver search failed"); + + // Hash + var hash = new sbyte[Sponge.HASH_LENGTH]; + + ICurl curl = SpongeFactory.Create(SpongeFactory.Mode.CURLP81); + curl.Reset(); + curl.Absorb(transactionTrits); + curl.Squeeze(hash); + + previousTransaction = Converter.ToTrytes(hash); + + response.Trytes.Add(Converter.ToTrytes(transactionTrits)); + } + + response.Trytes.Reverse(); + return response; + } + } +} diff --git a/Src/IotaSharp/Pow/Sponge.cs b/Src/IotaSharp/Pow/Sponge.cs new file mode 100644 index 0000000..0e503d5 --- /dev/null +++ b/Src/IotaSharp/Pow/Sponge.cs @@ -0,0 +1,92 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace IotaSharp.Pow +{ + /// + /// + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public abstract class Sponge : ICurl + { + /// + /// + /// + public const int HASH_LENGTH = 243; + + /// + /// + /// + /// + /// + /// + /// + public abstract ICurl Absorb(sbyte[] trits, int offset, int length); + + + /// + /// + /// + /// + /// + public ICurl Absorb(sbyte[] trits) + { + return Absorb(trits, 0, trits.Length); + } + + /// + /// + /// + /// + /// + /// + /// + public abstract sbyte[] Squeeze(sbyte[] trits, int offset, int length); + + + /// + /// + /// + /// + /// + public sbyte[] Squeeze(sbyte[] trits) + { + return Squeeze(trits, 0, trits.Length); + } + + /// + /// + /// + /// + public abstract ICurl Reset(); + + /// + /// + /// + /// + public abstract ICurl Clone(); + + /// + /// for Curl + /// + public sbyte[] State { get; set; } + + /// + /// + /// + public sbyte[] Rate + { + get + { + if (State != null && State.Length >= HASH_LENGTH) + { + sbyte[] rate = new sbyte[HASH_LENGTH]; + Array.Copy(State, rate, HASH_LENGTH); + return rate; + } + + return null; + } + } + } +} diff --git a/Src/IotaSharp/Pow/SpongeFactory.cs b/Src/IotaSharp/Pow/SpongeFactory.cs new file mode 100644 index 0000000..89d2871 --- /dev/null +++ b/Src/IotaSharp/Pow/SpongeFactory.cs @@ -0,0 +1,44 @@ +using System.Diagnostics.CodeAnalysis; + +namespace IotaSharp.Pow +{ + /// + /// + /// + [SuppressMessage("ReSharper", "IdentifierTypo")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public abstract class SpongeFactory + { + /// + /// + /// + public enum Mode + { +#pragma warning disable CS1591 + CURLP81, + CURLP27, + KERL, +#pragma warning restore CS1591 + } + + /// + /// + /// + /// + /// + public static ICurl Create(Mode mode) + { + switch (mode) + { + case Mode.CURLP81: + return new Curl(mode); + case Mode.CURLP27: + return new Curl(mode); + case Mode.KERL: + return new Kerl(); + default: + return null; + } + } + } +} diff --git a/Src/IotaSharp/Properties/AssemblyInfo.cs b/Src/IotaSharp/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dbd74eb --- /dev/null +++ b/Src/IotaSharp/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 + +//[assembly: AssemblyDescription("Iota Library")] +//[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +// [assembly: Guid("d7602b86-338c-4b56-ada4-d941305799c1")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +// [assembly: AssemblyVersion("0.9.0.0")] +// [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Src/IotaSharp/Utils/ArrayUtils.cs b/Src/IotaSharp/Utils/ArrayUtils.cs new file mode 100644 index 0000000..648e31a --- /dev/null +++ b/Src/IotaSharp/Utils/ArrayUtils.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; + +namespace IotaSharp.Utils +{ + /// + /// + /// + public class ArrayUtils + { + /// + /// + /// + /// + /// + /// + /// + public static IEnumerable SliceRow(T[,] array, int row) + { + for (var i = array.GetLowerBound(1); i <= array.GetUpperBound(1); i++) + { + yield return array[row, i]; + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static T[] SubArray(T[] data, int startIndex, int endIndex) + { + int length = endIndex - startIndex; + T[] result = new T[endIndex - startIndex]; + Array.Copy(data, startIndex, result, 0, length); + return result; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static T[] SubArray2(T[] data, int index, int length) + { + T[] result = new T[length]; + Array.Copy(data, index, result, 0, length); + return result; + } + } +} diff --git a/Src/IotaSharp/Utils/BundleValidator.cs b/Src/IotaSharp/Utils/BundleValidator.cs new file mode 100644 index 0000000..1f4e057 --- /dev/null +++ b/Src/IotaSharp/Utils/BundleValidator.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using IotaSharp.Model; +using IotaSharp.Pow; + +namespace IotaSharp.Utils +{ + /// + /// + /// + public class BundleValidator + { + /// + /// + /// + /// + /// + /// + public static bool IsBundle(Bundle bundle, ICurl curl = null) + { + if (curl == null) + curl = SpongeFactory.Create(SpongeFactory.Mode.KERL); + + + long totalSum = 0; + int lastIndex = bundle.Length - 1; + + for (int i = 0; i < bundle.Length; i++) + { + var tx = bundle.Transactions[i]; + totalSum += tx.Value; + + if (tx.CurrentIndex != i) + { + throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR); + } + if (tx.LastIndex != lastIndex) + { + throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR); + } + + sbyte[] txTrits = Converter.ToTrits(tx.ToTrytes().Substring(2187, 162)); + curl.Absorb(txTrits); + + // continue if output or signature tx + if (tx.Value>= 0) + { + continue; + } + + // here we have an input transaction (negative value) + List fragments = new List {tx.SignatureMessageFragment}; + + // find the subsequent txs containing the remaining signature + // message fragments for this input transaction + for (int j = i; j < bundle.Length - 1; j++) + { + Transaction tx2 = bundle.Transactions[j+1]; + + // check if the tx is part of the input transaction + if (tx.Address.Equals(tx2.Address, StringComparison.Ordinal) && tx2.Value == 0) + { + // append the signature message fragment + fragments.Add(tx2.SignatureMessageFragment); + } + } + + bool valid = new Signing(curl.Clone()).ValidateSignatures(tx.Address, fragments.ToArray(), tx.Bundle); + if (!valid) + { + throw new ArgumentException(Constants.INVALID_SIGNATURES_ERROR); + } + } + + // sum of all transaction must be 0 + if (totalSum != 0) + { + throw new ArgumentException(Constants.INVALID_BUNDLE_SUM_ERROR); + } + + sbyte[] bundleHashTrits = new sbyte[Sponge.HASH_LENGTH]; + curl.Squeeze(bundleHashTrits, 0, Sponge.HASH_LENGTH); + string bundleHash = Converter.ToTrytes(bundleHashTrits); + + if (!bundleHash.Equals(bundle.Transactions[0].Bundle, StringComparison.Ordinal)) + { + throw new ArgumentException(Constants.INVALID_BUNDLE_HASH_ERROR); + } + return true; + } + } +} diff --git a/Src/IotaSharp/Utils/Checksum.cs b/Src/IotaSharp/Utils/Checksum.cs new file mode 100644 index 0000000..941cbac --- /dev/null +++ b/Src/IotaSharp/Utils/Checksum.cs @@ -0,0 +1,69 @@ +using System; +using IotaSharp.Pow; + +namespace IotaSharp.Utils +{ + /// + /// This class defines utility methods to add/remove the checksum to/from an address. + /// + public static class Checksum + { + /// + /// Adds the checksum to the specified address + /// + /// An address without checksum + /// The address with the appended checksum + public static string AddChecksum(this string address) + { + InputValidator.CheckAddressWithoutChecksum(address); + var addressWithChecksum = address; + addressWithChecksum += CalculateChecksum(address); + return addressWithChecksum; + } + + /// + /// Removes the checksum from the specified address with checksum + /// + /// The address with checksum or without checksum. + /// the specified address without checksum + + public static string RemoveChecksum(this string address) + { + if (InputValidator.IsAddress(address)) return RemoveChecksumFromAddress(address); + + if (InputValidator.IsAddressWithoutChecksum(address)) return address; + + throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR); + } + + /// + /// Determines whether the specified address with checksum has a valid checksum. + /// + /// + /// + public static bool IsValidChecksum(this string addressWithChecksum) + { + var addressWithoutChecksum = RemoveChecksum(addressWithChecksum); + var addressWithRecalculateChecksum = addressWithoutChecksum + CalculateChecksum(addressWithoutChecksum); + return addressWithRecalculateChecksum.Equals(addressWithChecksum, StringComparison.Ordinal); + } + + private static string CalculateChecksum(string address) + { + ICurl curl = SpongeFactory.Create(SpongeFactory.Mode.KERL); + curl.Reset(); + curl.Absorb(Converter.ToTrits(address)); + sbyte[] checksumTrits = new sbyte[Sponge.HASH_LENGTH]; + curl.Squeeze(checksumTrits); + string checksum = Converter.ToTrytes(checksumTrits); + return checksum.Substring(72, 9); + } + + + + private static string RemoveChecksumFromAddress(string addressWithChecksum) + { + return addressWithChecksum.Substring(0, Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM); + } + } +} diff --git a/Src/IotaSharp/Utils/Constants.cs b/Src/IotaSharp/Utils/Constants.cs new file mode 100644 index 0000000..c8b62f3 --- /dev/null +++ b/Src/IotaSharp/Utils/Constants.cs @@ -0,0 +1,94 @@ +using System.Diagnostics.CodeAnalysis; + +namespace IotaSharp.Utils +{ + /// + /// + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class Constants + { + /// + /// + /// + public const string NULL_HASH = + "999999999999999999999999999999999999999999999999999999999999999999999999999999999"; + + /// + /// This String contains all possible characters of the tryte alphabet + /// + public static readonly string TRYTE_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /// + /// The length of an IOTA seed + /// + public const int SEED_LENGTH_MAX = 81; + + /// + /// The length of an address with checksum + /// + public const int ADDRESS_LENGTH_WITH_CHECKSUM = 90; + + /// + /// The length of an address without checksum + /// + public const int ADDRESS_LENGTH_WITHOUT_CHECKSUM = 81; + + /// + /// + /// + public const int MESSAGE_LENGTH = 2187; + + /// + /// The length of a transaction + /// + public const int TRANSACTION_LENGTH = 2673; + + /// + /// The length of an tag in trytes + /// + public const int TAG_LENGTH = 27; + + /// + /// + /// + public const int MIN_SECURITY_LEVEL = 1; + + /// + /// + /// + public const int MAX_SECURITY_LEVEL = 3; + + /// + /// + /// + public const int KEY_LENGTH = 6561; + +#pragma warning disable CS1591 + public const string INVALID_TAG_INPUT_ERROR = "Invalid tag provided."; + public const string INVALID_TRYTES_INPUT_ERROR = "Invalid trytes provided."; + public const string INVALID_TRANSFERS_INPUT_ERROR = "Invalid transfers provided."; + public const string INVALID_ADDRESSES_INPUT_ERROR = "Invalid addresses provided."; + public const string INVALID_SEED_INPUT_ERROR = "Invalid seed provided."; + public const string INVALID_SECURITY_LEVEL_INPUT_ERROR = "Invalid security level provided."; + public const string INVALID_BUNDLE_HASH_ERROR = "Invalid bundle hash."; + public const string INVALID_HASH_INPUT_ERROR = "Invalid hash provided."; + public const string INVALID_HASHES_INPUT_ERROR = "Invalid hashes provided."; + public const string INVALID_ATTACHED_TRYTES_INPUT_ERROR = "Invalid attached trytes provided."; + + public const string NOT_ENOUGH_BALANCE_ERROR = "Not enough balance."; + public const string INVALID_BUNDLE_ERROR = "Invalid bundle."; + public const string INVALID_BUNDLE_SUM_ERROR = "Invalid bundle sum."; + public const string INVALID_SIGNATURES_ERROR = "Invalid signatures."; + public const string INVALID_TAIL_HASH_INPUT_ERROR = "Invalid tail hash provided."; + public const string INVALID_INPUT_ERROR = "Invalid input provided."; + + public const string GET_TRYTES_RESPONSE_ERROR = "Get trytes response was null."; + public const string GET_BUNDLE_RESPONSE_ERROR = "Get bundle response was null."; + public const string SENDING_TO_USED_ADDRESS_ERROR = "Sending to a used address."; + public const string GET_INCLUSION_STATE_RESPONSE_ERROR = "Get inclusion state response was null."; +#pragma warning restore CS1591 + + } +} diff --git a/Src/IotaSharp/Utils/Converter.cs b/Src/IotaSharp/Utils/Converter.cs new file mode 100644 index 0000000..72bc0fd --- /dev/null +++ b/Src/IotaSharp/Utils/Converter.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; + +namespace IotaSharp.Utils +{ + /// + /// + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class Converter + { + private const sbyte RADIX = 3; + private const sbyte MAX_TRIT_VALUE = (RADIX - 1) / 2, MIN_TRIT_VALUE = -MAX_TRIT_VALUE; + + private const int NUMBER_OF_TRITS_IN_A_BYTE = 5; + private const int NUMBER_OF_TRITS_IN_A_TRYTE = 3; + private static readonly sbyte[][] BYTE_TO_TRITS_MAPPINGS = new sbyte[243][]; + private static readonly sbyte[][] TRYTE_TO_TRITS_MAPPINGS = new sbyte[27][]; + + static Converter() + { + sbyte[] trits = new sbyte[NUMBER_OF_TRITS_IN_A_BYTE]; + + for (int i = 0; i < 243; i++) + { + BYTE_TO_TRITS_MAPPINGS[i] = new sbyte[NUMBER_OF_TRITS_IN_A_BYTE]; + Array.Copy(trits, BYTE_TO_TRITS_MAPPINGS[i], NUMBER_OF_TRITS_IN_A_BYTE); + Increment(trits, NUMBER_OF_TRITS_IN_A_BYTE); + } + + for (int i = 0; i < 27; i++) + { + TRYTE_TO_TRITS_MAPPINGS[i] = new sbyte[NUMBER_OF_TRITS_IN_A_TRYTE]; + Array.Copy(trits, TRYTE_TO_TRITS_MAPPINGS[i], NUMBER_OF_TRITS_IN_A_TRYTE); + Increment(trits, NUMBER_OF_TRITS_IN_A_TRYTE); + } + } + + /// + /// + /// + /// + /// + public static sbyte[] ToTrits(string trytes) + { + sbyte[] d = new sbyte[3 * trytes.Length]; + for (int i = 0; i < trytes.Length; i++) + { + Array.Copy(TRYTE_TO_TRITS_MAPPINGS[Constants.TRYTE_ALPHABET.IndexOf(trytes[i])], 0, d, + i * NUMBER_OF_TRITS_IN_A_TRYTE, NUMBER_OF_TRITS_IN_A_TRYTE); + } + + return d; + } + + /// + /// + /// + /// + /// + /// + public static sbyte[] ToTrits(string trytes, int length) + { + var trits = ToTrits(trytes); + sbyte[] result = new sbyte[length]; + + int copyLength = trits.Length < length ? trits.Length : length; + + Array.Copy(trits, result, copyLength); + + return result; + } + /// + /// + /// + /// + /// + /// + public static sbyte[] ToTrits(long value, int length) + { + var trits = ToTrits(value); + sbyte[] result = new sbyte[length]; + + int copyLength = trits.Length < length ? trits.Length : length; + + Array.Copy(trits, result, copyLength); + + return result; + } + + /// + /// + /// + /// + /// + public static sbyte[] ToTrits(long value) + { + if (value == 0) + return new[] {(sbyte) 0}; + + var tritsList = new List(); + + while (value != 0) + { + var remainder = (sbyte) (value % RADIX); + value /= RADIX; + + if (remainder > MAX_TRIT_VALUE) + { + remainder = MIN_TRIT_VALUE; + value += 1; + } + else if (remainder < MIN_TRIT_VALUE) + { + remainder = MAX_TRIT_VALUE; + value -= 1; + } + + tritsList.Add(remainder); + } + + return tritsList.ToArray(); + } + + /// + /// + /// + /// + /// + public static string ToTrytes(sbyte[] trits) + { + return ToTrytes(trits, 0, trits.Length); + } + + /// + /// Converts the trits array to a trytes string + /// + /// The trits. + /// The offset from which copying is started. + /// The size. + /// a trytes string + public static string ToTrytes(sbyte[] trits, int offset, int size) + { + StringBuilder trytes = new StringBuilder(); + for (int i = 0; i < (size + NUMBER_OF_TRITS_IN_A_TRYTE - 1) / NUMBER_OF_TRITS_IN_A_TRYTE; i++) + { + int j = trits[offset + i * 3] + trits[offset + i * 3 + 1] * 3 + trits[offset + i * 3 + 2] * 9; + if (j < 0) + { + j += Constants.TRYTE_ALPHABET.Length; + } + + trytes.Append(Constants.TRYTE_ALPHABET[j]); + } + + return trytes.ToString(); + } + + /// + /// Increments the specified trits. + /// + /// The trits. + /// The size. + public static void Increment(sbyte[] trits, int size) + { + for (int i = 0; i < size; i++) + { + if (++trits[i] > MAX_TRIT_VALUE) + { + trits[i] = MIN_TRIT_VALUE; + } + else + { + break; + } + } + } + + /// + /// + /// + /// + /// + public static int ToInt32(sbyte[] trits) + { + int value = 0; + + for (int i = trits.Length; i-- > 0;) + { + value = value * 3 + trits[i]; + } + + return value; + } + + /// + /// + /// + /// + /// + public static long ToInt64(sbyte[] trits) + { + long value = 0; + + for (int i = trits.Length; i-- > 0;) + { + value = value * 3 + trits[i]; + } + + return value; + } + + /// + /// + /// + /// + /// + /// + /// + public static long ToInt64(sbyte[] trits, int index, int length) + { + long value = 0; + + for (int i = index + length - 1; i >= index; i--) + { + value = value * 3 + trits[i]; + } + + return value; + } + } +} diff --git a/IotaCSharpApi/Api/Utils/FixedBigIntConverter.cs b/Src/IotaSharp/Utils/FixedBigIntConverter.cs similarity index 93% rename from IotaCSharpApi/Api/Utils/FixedBigIntConverter.cs rename to Src/IotaSharp/Utils/FixedBigIntConverter.cs index 9aa0aed..abe1cb4 100644 --- a/IotaCSharpApi/Api/Utils/FixedBigIntConverter.cs +++ b/Src/IotaSharp/Utils/FixedBigIntConverter.cs @@ -1,8 +1,8 @@ using System; using Iesi.Collections.Generic; -using Iota.Lib.CSharp.Api.Exception; +using IotaSharp.Exception; -namespace Iota.Lib.CSharp.Api.Utils +namespace IotaSharp.Utils { /// /// @@ -19,12 +19,12 @@ public class FixedBigIntConverter /// hex representation of (3^242-1)/2 private static readonly int[] Half3 = { - unchecked((int) 0xa5ce8964), unchecked ((int) 0x9f007669), + unchecked((int) 0xa5ce8964), unchecked((int) 0x9f007669), 0x1484504f, 0x3ade00d9, 0x0c24486e, 0x50979d57, 0x79a4c702, 0x48bbae36, - unchecked ((int) 0xa9f6808b), unchecked ((int) 0xaa06a805), - unchecked ((int) 0xa87fabdf), 0x5e69ebef + unchecked((int) 0xa9f6808b), unchecked((int) 0xaa06a805), + unchecked((int) 0xa87fabdf), 0x5e69ebef }; /// @@ -32,7 +32,7 @@ public class FixedBigIntConverter /// /// /// - public static void FromTritsToBytes(int[] trits, byte[] bytes) + public static void FromTritsToBytes(sbyte[] trits, byte[] bytes) { if (trits.Length != TritsLength) throw new ArgumentException("trits array has invalid size"); @@ -42,7 +42,7 @@ public static void FromTritsToBytes(int[] trits, byte[] bytes) var baseHalf3 = new int[IntLength]; - var setUniqueNumbers = new LinkedHashSet(); + var setUniqueNumbers = new LinkedHashSet(); foreach (var x in trits) setUniqueNumbers.Add(x); if (setUniqueNumbers.Count == 1 && setUniqueNumbers.Contains(-1)) @@ -118,7 +118,7 @@ public static void FromTritsToBytes(int[] trits, byte[] bytes) /// /// /// - public static void FromBytesToTrits(byte[] bytes, int[] trits) + public static void FromBytesToTrits(byte[] bytes, sbyte[] trits) { if (bytes.Length != ByteLength) throw new ArgumentException("bytes array has invalid size"); @@ -138,7 +138,7 @@ public static void FromBytesToTrits(byte[] bytes, int[] trits) if (BigIntCmp(bigIntValue, Half3) == 0) { - var val = 0; + sbyte val = 0; if (bigIntValue[0] > 0) val = -1; else if (bigIntValue[0] < 0) val = 1; @@ -190,12 +190,12 @@ public static void FromBytesToTrits(byte[] bytes, int[] trits) remainder = r; } } - trits[i] = remainder - 1; + trits[i] = (sbyte) (remainder - 1); } if (flipTrits) for (var i = 0; i < TritsLength; i++) - trits[i] = -trits[i]; + trits[i] = (sbyte) -trits[i]; } } @@ -283,7 +283,7 @@ private static int[] BigIntSub(int[] lh, int[] rh) noborrow = ret.Item2; } - if (!noborrow) throw new IllegalStateException("noborrow"); + if (!noborrow) throw new IllegalStateException("no borrow"); return outValue; } @@ -309,4 +309,4 @@ private static Tuple FullAdd(int ia, int ib, bool carry) #endregion } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Utils/InputValidator.cs b/Src/IotaSharp/Utils/InputValidator.cs new file mode 100644 index 0000000..e9d7dc4 --- /dev/null +++ b/Src/IotaSharp/Utils/InputValidator.cs @@ -0,0 +1,363 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; +using IotaSharp.Model; + +namespace IotaSharp.Utils +{ + /// + /// + /// + public class InputValidator + { + /// + /// + /// + /// + /// + public static bool IsTrits(sbyte[] trits) + { + foreach (var trit in trits) + { + if (trit < -1 || trit > 1) + return false; + } + + return true; + } + + /// + /// Determines whether the specified string is an address. + /// Address must contain a checksum to be valid + /// + /// The address to validate. + /// + /// true if the specified string is an address; otherwise, false. + /// + public static bool IsAddress(string address) + { + return address.Length == Constants.ADDRESS_LENGTH_WITH_CHECKSUM && IsTrytes(address); + } + + /// + /// + /// + /// + /// + public static bool IsAddressWithoutChecksum(string address) + { + return address.Length == Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM && IsTrytes(address); + } + + /// + /// Checks whether the specified address is an address and throws and exception if the address is invalid. + /// Addresses must contain a checksum to be valid. + /// + /// + /// + public static bool CheckAddress(string address) + { + if (!IsAddress(address)) + throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR); + + return true; + } + + /// + /// + /// + /// + /// + public static bool CheckAddressWithoutChecksum(string address) + { + if (!IsAddressWithoutChecksum(address)) + { + throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR); + } + + return true; + } + + /// + /// Determines whether the specified string contains only characters from the trytes alphabet (see ) + /// + /// The trytes. + /// The length. + /// + /// true if the specified trytes are trytes otherwise, false. + /// + public static bool IsTrytes(string trytes, int length) + { + string regex = "^[9A-Z]{" + (length == 0 ? "0," : length.ToString(CultureInfo.InvariantCulture)) + "}$"; + var regexTrytes = new Regex(regex); + return regexTrytes.IsMatch(trytes); + } + + /// + /// + /// + /// + /// + public static bool IsTrytes(string trytes) + { + return IsTrytes(trytes, trytes.Length); + } + + + + /// + /// Determines whether the specified string represents an integer value. + /// + /// The value. + /// + /// true the specified string represents an integer value; otherwise, false. + /// + public static bool IsValue(string value) + { + return long.TryParse(value, out _); + } + + /// + /// Checks if input is correct hash. + /// + /// + /// + public static bool IsHash(string hash) + { + if (hash.Length == Constants.ADDRESS_LENGTH_WITH_CHECKSUM) + { + if (!IsTrytes(hash, Constants.ADDRESS_LENGTH_WITH_CHECKSUM)) + { + return false; + } + } + else + { + if (!IsTrytes(hash, Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM)) + { + return false; + } + } + + return true; + } + + /// + /// Determines whether the specified array contains only valid hashes. + /// + /// + /// + public static bool IsArrayOfHashes(string[] hashes) + { + if (hashes == null) + return false; + + foreach (var hash in hashes) + { + if (!IsHash(hash)) + return false; + } + + return true; + } + + /// + /// Determines whether the specified string array contains only trytes + /// + /// + /// + public static bool IsArrayOfTrytes(string[] trytes) + { + return IsArrayOfTrytes(trytes, Constants.TRANSACTION_LENGTH); + } + + /// + /// Determines whether the specified string array contains only trytes. + /// + /// + /// The length each String should be + /// + private static bool IsArrayOfTrytes(string[] trytes, int length) + { + return trytes.ToList().TrueForAll(element => IsTrytes(element, length)); + } + + /// + /// + /// + /// + /// + public static bool IsArrayOfRawTransactionTrytes(string[] trytes) + { + foreach (var tryte in trytes) + { + // This part of the value trits exceed iota max supply when used + if (!IsNinesTrytes(tryte.Substring(2279, 2295 - 2279), 16)) + { + return false; + } + } + + return IsArrayOfTrytes(trytes, Constants.TRANSACTION_LENGTH); + } + + /// + /// Determines whether the specified string consist only of '9'. + /// + /// The trytes. + /// The length. + /// + /// true if the specified string consist only of '9'; otherwise, false. + /// + public static bool IsNinesTrytes(string trytes, int length) + { + return Regex.IsMatch(trytes, + "^[9]{" + (length == 0 ? "0," : length.ToString(CultureInfo.InvariantCulture)) + "}$"); + } + + /// + /// Checks if the tag is valid. + /// Alias of IsValidTag + /// + /// + /// + public static bool IsTag(string tag) + { + return IsValidTag(tag); + } + + /// + /// Checks if the tag is valid. The string must not be empty and must contain trytes. + /// + /// + /// + private static bool IsValidTag(string tag) + { + return tag != null && tag.Length <= Constants.TAG_LENGTH && IsTrytes(tag); + } + + /// + /// Determines whether the specified transfer is valid. + /// + /// + /// + public static bool IsValidTransfer(Transfer transfer) + { + + if (transfer == null) + { + return false; + } + + if (!IsAddress(transfer.Address)) + { + return false; + } + + // Check if message is correct trytes encoded of any length + if (transfer.Message == null || !IsTrytes(transfer.Message, transfer.Message.Length)) + { + return false; + } + + // Check if tag is correct trytes encoded and not longer than 27 trytes + if (!IsTag(transfer.Tag)) + { + return false; + } + + return true; + } + + /// + /// Determines whether the specified transfers are valid. + /// + /// + /// + public static bool IsValidTransfersCollection(List transfers) + { + // Input validation of transfers object + if (transfers == null || transfers.Count == 0) + return false; + + foreach (var transfer in transfers) + { + if (!IsValidTransfer(transfer)) + return false; + } + + return true; + } + + /// + /// + /// + /// + /// + public static bool IsValidInputsCollection(Input[] inputs) + { + if (inputs == null || inputs.Length == 0) + return false; + + foreach (var input in inputs) + { + if (!IsValidInput(input)) + return false; + } + + return true; + } + + /// + /// + /// + /// + /// + public static bool IsValidInput(Input input) + { + if (input == null) + return false; + + if (!IsAddress(input.Address)) + return false; + + if (input.KeyIndex < 0) + return false; + + return IsValidSecurityLevel(input.Security); + } + + /// + /// + /// + /// + /// + public static bool IsValidSeed(string seed) + { + return seed.Length <= Constants.SEED_LENGTH_MAX && IsTrytes(seed); + } + + /// + /// + /// + /// + /// + public static bool IsValidSecurityLevel(int level) + { + return level >= Constants.MIN_SECURITY_LEVEL && level <= Constants.MAX_SECURITY_LEVEL; + } + + /// + /// + /// + /// + /// + public static string PadSeedIfNecessary(string seed) + { + while (seed.Length < Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM) seed += '9'; + return seed; + } + } +} diff --git a/Src/IotaSharp/Utils/IotaApiUtils.cs b/Src/IotaSharp/Utils/IotaApiUtils.cs new file mode 100644 index 0000000..114d91c --- /dev/null +++ b/Src/IotaSharp/Utils/IotaApiUtils.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using IotaSharp.Model; +using IotaSharp.Pow; + +namespace IotaSharp.Utils +{ + /// + /// + /// + public class IotaApiUtils + { + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static string NewAddress(string seed, int security, int index, bool checksum, ICurl curl) + { + if (!InputValidator.IsValidSecurityLevel(security)) + throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR); + + Signing signing = new Signing(curl); + sbyte[] key = signing.Key(Converter.ToTrits(seed), index, security); + sbyte[] digests = signing.Digests(key); + sbyte[] addressTrits = signing.Address(digests); + + string address = Converter.ToTrytes(addressTrits); + + if (checksum) + address = address.AddChecksum(); + + return address; + } + + + internal static List SignInputsAndReturn(string seed, + List inputs, + Bundle bundle, + List signatureFragments, ICurl curl) + { + bundle.FinalizeBundle(curl); + bundle.AddTrytes(signatureFragments); + + // SIGNING OF INPUTS + // + // Here we do the actual signing of the inputs + // Iterate over all bundle transactions, find the inputs + // Get the corresponding private key and calculate the signatureFragment + for (int i = 0; i < bundle.Transactions.Count; i++) + { + if (bundle.Transactions[i].Value < 0) + { + string thisAddress = bundle.Transactions[i].Address; + + // Get the corresponding keyIndex of the address + int keyIndex = 0; + int keySecurity = 0; + foreach (Input input in inputs) + { + if (input.Address.StartsWith(thisAddress, StringComparison.Ordinal)) + { + keyIndex = input.KeyIndex; + keySecurity = input.Security; + break; + } + } + + string bundleHash = bundle.Transactions[i].Bundle; + + // Get corresponding private key of address + sbyte[] key = new Signing(curl).Key(Converter.ToTrits(seed), keyIndex, keySecurity); + + // First 6561 trits for the firstFragment + //sbyte[] firstFragment = ArrayUtils.SubArray2(key, 0, 6561); + + // Get the normalized bundle hash + sbyte[] normalizedBundleHash = bundle.NormalizedBundle(bundleHash); + + // First bundle fragment uses 27 trytes + //sbyte[] firstBundleFragment = ArrayUtils.SubArray2(normalizedBundleHash, 0, 27); + + // Calculate the new signatureFragment with the first bundle fragment + //sbyte[] firstSignedFragment = + // new Signing(curl).SignatureFragment(firstBundleFragment, firstFragment); + + // Convert signature to trytes and assign the new signatureFragment + //bundle.Transactions[i].SignatureMessageFragment = Converter.ToTrytes(firstSignedFragment); + + + // if user chooses higher than 27-tryte security + // for each security level, add an additional signature + for (int j = 0; j < keySecurity; j++) + { + int hashPart = j % 3; + // Add parts of signature for bundles with same address + if (bundle.Transactions[i + j].Address.StartsWith(thisAddress, StringComparison.Ordinal)) + { + // Use 6562 trits starting from j*6561 + sbyte[] keyFragment = ArrayUtils.SubArray2(key, 6561 * j, 6561); + + // The current part of the bundle hash + sbyte[] bundleFragment = + ArrayUtils.SubArray2(normalizedBundleHash, 27 * hashPart, 27); + + // Calculate the new signature + sbyte[] signedFragment = new Signing(curl).SignatureFragment(bundleFragment, + keyFragment); + + // Convert signature to trytes and assign it again to this bundle entry + bundle.Transactions[i + j].SignatureMessageFragment = + (Converter.ToTrytes(signedFragment)); + } + } + + } + } + + List bundleTrytes = new List(); + + // Convert all bundle entries into trytes + foreach (Transaction tx in bundle.Transactions) + { + bundleTrytes.Add(tx.ToTrytes()); + } + + bundleTrytes.Reverse(); + return bundleTrytes; + } + } +} diff --git a/Src/IotaSharp/Utils/IotaUnitConverter.cs b/Src/IotaSharp/Utils/IotaUnitConverter.cs new file mode 100644 index 0000000..5f7eeea --- /dev/null +++ b/Src/IotaSharp/Utils/IotaUnitConverter.cs @@ -0,0 +1,68 @@ +using System; + +namespace IotaSharp.Utils +{ + /// + /// This class provides methods to convert Iota to different units. + /// + public class IotaUnitConverter + { + /// + /// Convert the iota amount + /// + /// amount + /// the source unit e.g. the unit of amount + /// the target unit + /// the specified amount in the target unit + public static double ConvertUnits(long amount, IotaUnits fromUnit, IotaUnits toUnit) + { + long amountInSource = amount * fromUnit.Value; + return (double) amountInSource / toUnit.Value; + } + + /// + /// Finds the optimal unit to display the specified amount in + /// + /// amount + /// the optimal IotaUnit + public static IotaUnits FindOptimalIotaUnitToDisplay(long amount) + { + amount = Math.Abs(amount); + + if (amount < IotaUnits.KiloIOTA.Value) + return IotaUnits.IOTA; + + if (amount < IotaUnits.MegaIOTA.Value) + return IotaUnits.KiloIOTA; + + if (amount < IotaUnits.GigaIOTA.Value) + return IotaUnits.MegaIOTA; + + if (amount < IotaUnits.TerraIOTA.Value) + return IotaUnits.GigaIOTA; + + if (amount < IotaUnits.PetaIOTA.Value) + return IotaUnits.TerraIOTA; + + return IotaUnits.PetaIOTA; + + } + + /// + /// Convert the iota amount to text. + /// + /// + /// + /// + public static string ConvertRawIotaAmountToDisplayText(long amount, bool extended) + { + var unit = FindOptimalIotaUnitToDisplay(amount); + double amountInDisplayUnit = (double) amount / unit.Value; + + if (extended) + return $"{amountInDisplayUnit:##0.##################} {unit.Unit}"; + + return $"{amountInDisplayUnit:##0.##} {unit.Unit}"; + } + } +} diff --git a/Src/IotaSharp/Utils/IotaUnits.cs b/Src/IotaSharp/Utils/IotaUnits.cs new file mode 100644 index 0000000..dc9dfdf --- /dev/null +++ b/Src/IotaSharp/Utils/IotaUnits.cs @@ -0,0 +1,54 @@ +namespace IotaSharp.Utils +{ + /// + /// Table of IOTA units based off of the standard system of Units. + /// + public class IotaUnits + { + /// + /// 10^0 + /// + public static readonly IotaUnits IOTA = new IotaUnits("i", 1L); + + /// + /// 10^3 + /// + public static readonly IotaUnits KiloIOTA = new IotaUnits("Ki", IOTA.Value * 1000L); + + /// + /// 10^6 + /// + public static readonly IotaUnits MegaIOTA = new IotaUnits("Mi", KiloIOTA.Value * 1000L); + + /// + /// 10^9 + /// + public static readonly IotaUnits GigaIOTA = new IotaUnits("Gi", MegaIOTA.Value * 1000L); + + /// + /// 10^12 + /// + public static readonly IotaUnits TerraIOTA = new IotaUnits("Ti", GigaIOTA.Value * 1000L); + + /// + /// 10^15 + /// + public static readonly IotaUnits PetaIOTA = new IotaUnits("Pi", TerraIOTA.Value * 1000L); + + private IotaUnits(string unit, long value) + { + Unit = unit; + Value = value; + } + + /// + /// unit + /// + public string Unit { get; } + + /// + /// value + /// + public long Value { get; } + } +} diff --git a/Src/IotaSharp/Utils/JsonWebClient.cs b/Src/IotaSharp/Utils/JsonWebClient.cs new file mode 100644 index 0000000..7d93480 --- /dev/null +++ b/Src/IotaSharp/Utils/JsonWebClient.cs @@ -0,0 +1,91 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Cache; +using System.Text; +using IotaSharp.Core; +using IotaSharp.Exception; +using Newtonsoft.Json; + +namespace IotaSharp.Utils +{ + internal class JsonWebClient + { + public TResponse GetPOSTResponseSync(Uri uri, string data) + { + var request = WebRequest.CreateHttp(uri); + request.ContentType = "application/json"; + request.Accept = "application/json"; + request.Method = "POST"; + request.AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate); + request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore); + request.Headers.Add("X-IOTA-API-Version", "1"); + request.Headers.Add("Origin", "IotaSharp"); + request.KeepAlive = false; + request.Timeout = 300000; + request.ReadWriteTimeout = 300000; + + UTF8Encoding encoding = new UTF8Encoding(); + byte[] bytes = encoding.GetBytes(data); + request.ContentLength = bytes.Length; + + using (var requestStream = request.GetRequestStream()) + { + // Send the data. + requestStream.Write(bytes, 0, bytes.Length); + } + + try + { + using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) + { + using (Stream stream = response.GetResponseStream()) + { + Debug.Assert(stream != null, nameof(stream) + " != null"); + + StreamReader reader = new StreamReader(stream, Encoding.UTF8); + string responseString = reader.ReadToEnd(); + + if (response.StatusCode == HttpStatusCode.OK) + { + return JsonConvert.DeserializeObject(responseString); + } + + throw new IotaApiException(JsonConvert.DeserializeObject(responseString).Error); + } + } + } + catch (WebException ex) + { + if (ex.Response != null) + { + using (var stream = ex.Response.GetResponseStream()) + { + Debug.Assert(stream != null, nameof(stream) + " != null"); + + using (var reader = new StreamReader(stream)) + { + string errorResponse = reader.ReadToEnd(); + + HttpWebResponse response = (HttpWebResponse) ex.Response; + + if (response.StatusCode == HttpStatusCode.BadRequest) + throw new ArgumentException(errorResponse); + if (response.StatusCode == HttpStatusCode.Unauthorized) + throw new IllegalAccessException("401" + errorResponse); + if (response.StatusCode == HttpStatusCode.InternalServerError) + throw new IllegalAccessException("500" + errorResponse); + + + throw new IotaApiException( + JsonConvert.DeserializeObject(errorResponse).Error); + } + } + } + + throw; + } + } + } +} diff --git a/IotaCSharpApi/Api/Utils/Multisig.cs b/Src/IotaSharp/Utils/Multisig.cs similarity index 90% rename from IotaCSharpApi/Api/Utils/Multisig.cs rename to Src/IotaSharp/Utils/Multisig.cs index a39f087..a4a84c0 100644 --- a/IotaCSharpApi/Api/Utils/Multisig.cs +++ b/Src/IotaSharp/Utils/Multisig.cs @@ -1,10 +1,11 @@ using System; -using Iota.Lib.CSharp.Api.Model; -using Iota.Lib.CSharp.Api.Pow; +using IotaSharp.Model; +using IotaSharp.Pow; -namespace Iota.Lib.CSharp.Api.Utils +namespace IotaSharp.Utils { /// + /// /// public class Multisig { @@ -80,7 +81,7 @@ public void AddAddressDigest(string[] digests) /// address public string FinalizeAddress() { - var addressTrits = new int[243]; + var addressTrits = new sbyte[243]; _curl.Squeeze(addressTrits); // Convert trits into trytes and return the address @@ -92,18 +93,18 @@ public string FinalizeAddress() /// /// /// - public bool ValidateAddress(string multisigAddress, int[][] digests) + public bool ValidateAddress(string multisigAddress, sbyte[][] digests) { // initialize Curl with the provided state _curl.Reset(); foreach (var digest in digests) _curl.Absorb(digest); - var addressTrits = new int[243]; + var addressTrits = new sbyte[243]; _curl.Squeeze(addressTrits); // Convert trits into trytes and return the address - return Converter.ToTrytes(addressTrits).Equals(multisigAddress); + return Converter.ToTrytes(addressTrits).Equals(multisigAddress, StringComparison.Ordinal); } /// @@ -116,7 +117,7 @@ public Bundle AddSignature(Bundle bundleToSign, string inputAddress, string keyT { // Get the security used for the private key // 1 security level = 2187 trytes - var security = keyTrytes.Length / Constants.MessageLength; + var security = keyTrytes.Length / Constants.MESSAGE_LENGTH; // convert private key trytes into trits var key = Converter.ToTrits(keyTrytes); @@ -129,7 +130,7 @@ public Bundle AddSignature(Bundle bundleToSign, string inputAddress, string keyT for (var i = 0; i < bundleToSign.Transactions.Count; i++) - if (bundleToSign.Transactions[i].Address.Equals(inputAddress)) + if (bundleToSign.Transactions[i].Address.Equals(inputAddress, StringComparison.Ordinal)) if (!InputValidator.IsNinesTrytes(bundleToSign.Transactions[i].SignatureMessageFragment, bundleToSign.Transactions[i].SignatureMessageFragment.Length)) { @@ -141,12 +142,12 @@ public Bundle AddSignature(Bundle bundleToSign, string inputAddress, string keyT var bundleHash = bundleToSign.Transactions[i].Bundle; // First 6561 trits for the firstFragment - var firstFragment = new int[6561]; + var firstFragment = new sbyte[6561]; Array.Copy(key, firstFragment, 6561); // Get the normalized bundle hash - var normalizedBundleFragments = new int[3][]; - for (var n = 0; n < 3; n++) normalizedBundleFragments[n] = new int[27]; + var normalizedBundleFragments = new sbyte[3][]; + for (var n = 0; n < 3; n++) normalizedBundleFragments[n] = new sbyte[27]; var normalizedBundleHash = bundleToSign.NormalizedBundle(bundleHash); @@ -168,7 +169,7 @@ public Bundle AddSignature(Bundle bundleToSign, string inputAddress, string keyT for (var j = 1; j < security; j++) { // Next 6561 trits for the firstFragment - var nextFragment = new int[6561]; + var nextFragment = new sbyte[6561]; Array.Copy(key, 6561 * j, nextFragment, 0, 6561); // Use the next 27 trytes @@ -179,7 +180,8 @@ public Bundle AddSignature(Bundle bundleToSign, string inputAddress, string keyT // Convert signature to trytes and add new bundle entry at i + j position // Assign the signature fragment - bundleToSign.Transactions[i + j].SignatureMessageFragment = Converter.ToTrytes(nextSignedFragment); + bundleToSign.Transactions[i + j].SignatureMessageFragment = + Converter.ToTrytes(nextSignedFragment); } break; @@ -188,4 +190,4 @@ public Bundle AddSignature(Bundle bundleToSign, string inputAddress, string keyT return bundleToSign; } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Utils/SeedRandomGenerator.cs b/Src/IotaSharp/Utils/SeedRandomGenerator.cs new file mode 100644 index 0000000..452c7ea --- /dev/null +++ b/Src/IotaSharp/Utils/SeedRandomGenerator.cs @@ -0,0 +1,31 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace IotaSharp.Utils +{ + /// + /// + /// + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public class SeedRandomGenerator + { + /// + /// + /// + /// + public static string GenerateNewSeed() + { + return KeyGen(Constants.SEED_LENGTH_MAX); + } + + private static string KeyGen(int length) + { + var charset = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + var random = new Random((int) DateTime.Now.Ticks); + + return new string(Enumerable.Repeat(charset, length) + .Select(s => s[random.Next(s.Length)]).ToArray()); + } + } +} diff --git a/IotaApi.Standard/Utils/Signing.cs b/Src/IotaSharp/Utils/Signing.cs similarity index 75% rename from IotaApi.Standard/Utils/Signing.cs rename to Src/IotaSharp/Utils/Signing.cs index c836c4b..a975a56 100644 --- a/IotaApi.Standard/Utils/Signing.cs +++ b/Src/IotaSharp/Utils/Signing.cs @@ -1,23 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; -using Iota.Api.Standard.Model; -using Iota.Api.Standard.Pow; +using IotaSharp.Model; +using IotaSharp.Pow; -namespace Iota.Api.Standard.Utils +namespace IotaSharp.Utils { - //TODO(gjc): add comments - /// - /// Ask cfb + /// /// public class Signing { - /// - /// - /// - public static int KeyLength = 6561; - private readonly ICurl _curl; /// @@ -25,11 +18,7 @@ public class Signing /// public Signing(ICurl curl) { - // ReSharper disable once ConvertIfStatementToNullCoalescingExpression - if (curl == null) - _curl = new Kerl(); - else - _curl = curl; + _curl = curl ?? new Kerl(); } /// @@ -45,26 +34,26 @@ public Signing() /// /// /// - public int[] Key(int[] seed, int index, int security) + public sbyte[] Key(sbyte[] seed, int index, int security) { - var subseed = new int[seed.Length]; - seed.CopyTo(subseed, 0); + var subSeed = new sbyte[seed.Length]; + seed.CopyTo(subSeed, 0); for (var i = 0; i < index; i++) for (var j = 0; j < 243; j++) - if (++subseed[j] > 1) - subseed[j] = -1; + if (++subSeed[j] > 1) + subSeed[j] = -1; else break; _curl.Reset(); - _curl.Absorb(subseed, 0, subseed.Length); - _curl.Squeeze(subseed, 0, subseed.Length); + _curl.Absorb(subSeed, 0, subSeed.Length); + _curl.Squeeze(subSeed, 0, subSeed.Length); _curl.Reset(); - _curl.Absorb(subseed, 0, subseed.Length); + _curl.Absorb(subSeed, 0, subSeed.Length); - var key = new List(); - var buffer = new int[subseed.Length]; + var key = new List(); + var buffer = new sbyte[subSeed.Length]; var offset = 0; while (security-- > 0) @@ -81,14 +70,14 @@ public int[] Key(int[] seed, int index, int security) /// /// /// - public int[] Digests(int[] key) + public sbyte[] Digests(sbyte[] key) { - var digests = new int[(int) Math.Floor((decimal) key.Length / 6561) * 243]; - var buffer = new int[243]; + var digests = new sbyte[(int) Math.Floor((decimal) key.Length / 6561) * 243]; + var buffer = new sbyte[243]; for (var i = 0; i < Math.Floor((decimal) key.Length / 6561); i++) { - var keyFragment = new int[6561]; + var keyFragment = new sbyte[6561]; Array.Copy(key, i * 6561, keyFragment, 0, 6561); for (var j = 0; j < 27; j++) @@ -119,10 +108,10 @@ public int[] Digests(int[] key) /// /// /// - public int[] Digest(int[] normalizedBundleFragment, int[] signatureFragment) + public sbyte[] Digest(sbyte[] normalizedBundleFragment, sbyte[] signatureFragment) { _curl.Reset(); - var buffer = new int[243]; + sbyte[] buffer = null; for (var i = 0; i < 27; i++) { @@ -149,9 +138,9 @@ public int[] Digest(int[] normalizedBundleFragment, int[] signatureFragment) /// /// /// - public int[] Address(int[] digests) + public sbyte[] Address(sbyte[] digests) { - var address = new int[243]; + var address = new sbyte[243]; _curl.Reset(); _curl.Absorb(digests, 0, digests.Length); _curl.Squeeze(address, 0, address.Length); @@ -163,9 +152,9 @@ public int[] Address(int[] digests) /// /// /// - public int[] SignatureFragment(int[] normalizedBundleFragment, int[] keyFragment) + public sbyte[] SignatureFragment(sbyte[] normalizedBundleFragment, sbyte[] keyFragment) { - var hash = new int[243]; + var hash = new sbyte[243]; for (var i = 0; i < 27; i++) { @@ -177,9 +166,9 @@ public int[] SignatureFragment(int[] normalizedBundleFragment, int[] keyFragment _curl.Absorb(hash, 0, hash.Length); _curl.Squeeze(hash, 0, hash.Length); } - - for (var j = 0; j < 243; j++) Array.Copy(hash, j, keyFragment, i * 243 + j, 1); + //for (var j = 0; j < 243; j++) Array.Copy(hash, j, keyFragment, i * 243 + j, 1); + Array.Copy(hash, 0, keyFragment, i * 243, 243); } return keyFragment; @@ -194,22 +183,23 @@ public int[] SignatureFragment(int[] normalizedBundleFragment, int[] keyFragment /// public bool ValidateSignatures(Bundle signedBundle, string inputAddress) { - string bundleHash = ""; + var bundleHash = ""; - List signatureFragments = new List(); + var signatureFragments = new List(); foreach (var trx in signedBundle.Transactions) { - if (trx.Address.Equals(inputAddress)) + if (trx.Address.Equals(inputAddress, StringComparison.Ordinal)) { bundleHash = trx.Bundle; // if we reached remainder bundle - String signatureFragment = trx.SignatureMessageFragment; + var signatureFragment = trx.SignatureMessageFragment; if (InputValidator.IsNinesTrytes(signatureFragment, signatureFragment.Length)) { break; } + signatureFragments.Add(signatureFragment); } } @@ -227,7 +217,7 @@ public bool ValidateSignatures(string expectedAddress, string[] signatureFragmen { var bundle = new Bundle(); - var normalizedBundleFragments = new int[3, 27]; + var normalizedBundleFragments = new sbyte[3, 27]; var normalizedBundleHash = bundle.NormalizedBundle(bundleHash); // Split hash into 3 fragments @@ -237,25 +227,25 @@ public bool ValidateSignatures(string expectedAddress, string[] signatureFragmen //Array.Copy(normalizedBundleHash, i * 27, normalizedBundleFragments, 0, 27); for (var j = 0; j < 27; j++) { - normalizedBundleFragments[i, j] = normalizedBundleHash[i*27+j]; + normalizedBundleFragments[i, j] = normalizedBundleHash[i * 27 + j]; } } - + // Get digests - var digests = new int[signatureFragments.Length * 243]; + var digests = new sbyte[signatureFragments.Length * 243]; for (var i = 0; i < signatureFragments.Length; i++) { var digestBuffer = Digest(ArrayUtils.SliceRow(normalizedBundleFragments, i % 3).ToArray(), Converter.ToTrits(signatureFragments[i])); - + Array.Copy(digestBuffer, 0, digests, i * 243, 243); } var address = Converter.ToTrytes(Address(digests)); - return expectedAddress.Equals(address); + return expectedAddress.Equals(address, StringComparison.Ordinal); } } -} \ No newline at end of file +} diff --git a/Src/IotaSharp/Utils/TimeStamp.cs b/Src/IotaSharp/Utils/TimeStamp.cs new file mode 100644 index 0000000..3074bff --- /dev/null +++ b/Src/IotaSharp/Utils/TimeStamp.cs @@ -0,0 +1,15 @@ +using System; + +namespace IotaSharp.Utils +{ + /// + /// + /// + public class TimeStamp + { + internal static long Now() + { + return (long)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds; + } + } +} diff --git a/IotaApi.Standard/Utils/TrytesConverter.cs b/Src/IotaSharp/Utils/TrytesConverter.cs similarity index 83% rename from IotaApi.Standard/Utils/TrytesConverter.cs rename to Src/IotaSharp/Utils/TrytesConverter.cs index 4b802ab..08da295 100644 --- a/IotaApi.Standard/Utils/TrytesConverter.cs +++ b/Src/IotaSharp/Utils/TrytesConverter.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Iota.Api.Standard.Utils +namespace IotaSharp.Utils { /// /// This class allows to convert between ASCII and tryte encoded strings @@ -12,7 +12,7 @@ public class TrytesConverter /// /// ASCII encoded string /// tryte encoded string - public static string ToTrytes(string inputString) + public static string AsciiToTrytes(string inputString) { var trytes = new StringBuilder(); @@ -23,19 +23,19 @@ public static string ToTrytes(string inputString) // If not recognizable ASCII character, replace with space if (asciiValue > 255) asciiValue = ' '; - trytes.Append(Constants.TryteAlphabet[asciiValue % 27]); - trytes.Append(Constants.TryteAlphabet[asciiValue / 27]); + trytes.Append(Constants.TRYTE_ALPHABET[asciiValue % 27]); + trytes.Append(Constants.TRYTE_ALPHABET[asciiValue / 27]); } return trytes.ToString(); } - + /// /// Converts the specified tryte encoded String to ASCII /// /// tryte encoded string /// an ASCII encoded string - public static string ToString(string inputTrytes) + public static string TrytesToAscii(string inputTrytes) { var builder = new StringBuilder(); @@ -47,7 +47,7 @@ public static string ToString(string inputTrytes) var secondValue = TryteToDecimal(inputTrytes[i + 1]); var decimalValue = firstValue + secondValue * 27; - builder.Append((char) decimalValue); + builder.Append((char)decimalValue); } return builder.ToString(); @@ -66,4 +66,4 @@ public static int TryteToDecimal(char tryte) return tryte - 'A' + 1; } } -} \ No newline at end of file +} diff --git a/Src/NuGet.Config b/Src/NuGet.Config new file mode 100644 index 0000000..b04b006 --- /dev/null +++ b/Src/NuGet.Config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Src/global.json b/Src/global.json new file mode 100644 index 0000000..2be3e86 --- /dev/null +++ b/Src/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "2.2.105" + } +} \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 3bfea51..5c718e5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,10 @@ version: 1.0.{build} +nuget: + disable_publish_on_pr: true image: Visual Studio 2017 +configuration: Release before_build: - - nuget restore + - nuget restore Src\IotaSharp.sln build: - project: IotaApi.sln + project: Src\IotaSharp.sln verbosity: minimal \ No newline at end of file