* Fix ethereum explorer links
* Refactored-out chain specific share DTOs
* Moved ShareRecorder and ShareRelay to more fitting namespace
* Greatly reduce the number of threads for external stratum monitoring
* Add payload-type-flag-frame to share publisher/subscriber
* ProtoBuf Share Relay & Recorder
* Do not require monero wallet daemon config if payment processing is disabled
* Reconnect to share relay on receive timeout
* Cryptonote Tests
* Monero v7
This commit is contained in:
Oliver Weichhold 2018-03-16 17:37:49 +01:00 committed by GitHub
parent df5ecf205e
commit f511dd6736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
158 changed files with 5627 additions and 1730 deletions

1
.gitignore vendored
View File

@ -117,3 +117,4 @@ recovered-shares.json
src/MiningCore/config\.json
/src/MiningCore/config2.json

View File

@ -47,12 +47,12 @@ namespace MiningCore.Tests.Blockchain.Bitcoin
// set clock to submission time
clock.CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1508869907).UtcDateTime;
var share = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a");
var (share, blockHex) = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a");
Assert.NotNull(share);
Assert.True(share.IsBlockCandidate);
Assert.Equal(share.BlockHash, "000000000fccf11cd0b7d9057441e430c320384b95b034bd28092c4553594b4a");
Assert.Equal(share.BlockHex, "00000020bb76da6422b707a90831c421798123293bc5fd377bbeb51985570909000000008677145722cbe6f1ebec19fecc724cab5487f3292a69f6908bd512f645bb0635f286ef59ffff7f206aae848d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff295e0c0b2f454231362f414431322f04f286ef590801000058010000000c2f4d696e696e67436f72652f000000000100f2052a010000001976a9142ebb5cccf9a6bb927661d2953655c43c04accc3788ac00000000");
Assert.Equal(share.BlockHash, "601ed85039804bcecbbdb53e0ca358aeb8dabef2366fb64c216aac3aba02b716");
Assert.Equal(blockHex, "00000020bb76da6422b707a90831c421798123293bc5fd377bbeb5198557090900000000fd5418fe788ef961678e4bacdd1fe3903185b9ec63865bb3d2d279bb0eb48c0bf286ef59ffff7f206aae848d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff295e0c0b2f454231362f414431322f04f286ef590001000058010000000c2f4d696e696e67436f72652f000000000100f2052a010000001976a9142ebb5cccf9a6bb927661d2953655c43c04accc3788ac00000000");
Assert.Equal(share.BlockHeight, 14);
Assert.Equal(share.BlockReward, 50);
Assert.Equal(share.Difficulty, 0.5);
@ -87,14 +87,14 @@ namespace MiningCore.Tests.Blockchain.Bitcoin
Assert.Throws<StratumException>(() => job.ProcessShare(worker, "02000000", "59ef86f2", "8d84ae6a"));
// make sure we don't accept case-sensitive duplicate shares as basically 0xdeadbeaf = 0xDEADBEAF.
var share = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a");
var (share, blockHex) = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a");
Assert.Throws<StratumException>(() => job.ProcessShare(worker, "01000000", "59ef86f2", "8D84AE6A"));
// invalid time
Assert.Throws<StratumException>(() => job.ProcessShare(worker, "01000000", "69ef86f2", "8d84ae6a"));
// invalid nonce
Assert.Throws<StratumException>(() => job.ProcessShare(worker, "01000000", "59ef86f2", "ad84be6a"));
Assert.Throws<StratumException>(() => job.ProcessShare(worker, "01000000", "59ef86f2", "4a84be6a"));
// valid share data but invalid submission time
clock.CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1408869907).UtcDateTime;

View File

@ -26,12 +26,12 @@ namespace MiningCore.Tests.Blockchain.Monero
"{\"blocktemplate_blob\":\"0106e7eabdcf058234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d00000000019c0201ffe00106e3a1a0cc010275d92c0a057aa5f073079694a153d426f837f49fdb9654da10a5364e79a2086280a0d9e61d028b46dca0d04998500b40b046fd6f8bb33229e6380fd465dbb1327aa6f813d8bd80c0fc82aa0202372f076459e769116d604d30aabff7160782acc0d20e0c5cdc8963ed4e16372f8090cad2c60e02f009504ce65538bbb684b466b21be3a90e3740f185d7089d37b75f0cf62b6e7680e08d84ddcb0102cf01b85c0b592bb6e508e20b5d317052b75de121908390363201abff3476ef0180c0caf384a302024b81076c8ad0cfe84cc32fe0813d63cdd0f7d8d0e56d82aa3f58cbbe49d4c61e2b017aaf3074be7ecb30a769595758e4da7c7c87ead864baf89b679b73153dfa352c0208000000000000000000\",\"Difficulty\":2,\"Height\":224,\"prev_hash\":\"8234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d\",\"reserved_offset\":322,\"Status\":\"OK\"}");
var job = new MoneroJob(bt, "d150da".HexToByteArray(), "1", poolConfig, clusterConfig);
var share = job.ProcessShare("040100a4", 1, "f29c7fbf57d97eeedb61555857d7a34314250da20742b8157f96e0be89530a00", worker);
var (share, blobHex, blobHash) = job.ProcessShare("040100a4", 1, "f29c7fbf57d97eeedb61555857d7a34314250da20742b8157f96e0be89530a00", worker);
Assert.NotNull(share);
Assert.True(share.IsBlockCandidate);
Assert.Equal(share.BlobHash, "9258faf2dff5daf026681b5fa5d94a34dbb5bade1d9e2070865ba8c68f8f0454");
Assert.Equal(share.BlobHex, "0106e7eabdcf058234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d040100a4019c0201ffe00106e3a1a0cc010275d92c0a057aa5f073079694a153d426f837f49fdb9654da10a5364e79a2086280a0d9e61d028b46dca0d04998500b40b046fd6f8bb33229e6380fd465dbb1327aa6f813d8bd80c0fc82aa0202372f076459e769116d604d30aabff7160782acc0d20e0c5cdc8963ed4e16372f8090cad2c60e02f009504ce65538bbb684b466b21be3a90e3740f185d7089d37b75f0cf62b6e7680e08d84ddcb0102cf01b85c0b592bb6e508e20b5d317052b75de121908390363201abff3476ef0180c0caf384a302024b81076c8ad0cfe84cc32fe0813d63cdd0f7d8d0e56d82aa3f58cbbe49d4c61e2b017aaf3074be7ecb30a769595758e4da7c7c87ead864baf89b679b73153dfa352c02080000000001d150da00");
Assert.Equal(blobHash, "9258faf2dff5daf026681b5fa5d94a34dbb5bade1d9e2070865ba8c68f8f0454");
Assert.Equal(blobHex, "0106e7eabdcf058234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d040100a4019c0201ffe00106e3a1a0cc010275d92c0a057aa5f073079694a153d426f837f49fdb9654da10a5364e79a2086280a0d9e61d028b46dca0d04998500b40b046fd6f8bb33229e6380fd465dbb1327aa6f813d8bd80c0fc82aa0202372f076459e769116d604d30aabff7160782acc0d20e0c5cdc8963ed4e16372f8090cad2c60e02f009504ce65538bbb684b466b21be3a90e3740f185d7089d37b75f0cf62b6e7680e08d84ddcb0102cf01b85c0b592bb6e508e20b5d317052b75de121908390363201abff3476ef0180c0caf384a302024b81076c8ad0cfe84cc32fe0813d63cdd0f7d8d0e56d82aa3f58cbbe49d4c61e2b017aaf3074be7ecb30a769595758e4da7c7c87ead864baf89b679b73153dfa352c02080000000001d150da00");
Assert.Equal(share.BlockHeight, 224);
Assert.Equal(share.Difficulty, 1000);
}

View File

@ -0,0 +1,89 @@
using System;
using MiningCore.Extensions;
using MiningCore.Native;
using Xunit;
namespace MiningCore.Tests.Crypto
{
public class CrytonoteTests : TestBase
{
[Fact]
public void Crytonote_Hash_Slow()
{
var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray();
var result = LibCryptonote.CryptonightHashSlow(blobConverted, 0).ToHexString();
Assert.Equal("a845ffbdf83ae9a8ffa504a1011efbd5ed2294bb9da591d3b583740568402c00", result);
}
[Fact]
public void Cryptonote_SlowHash_Should_Throw_On_Null_Argument()
{
Assert.Throws<ArgumentNullException>(() => LibCryptonote.CryptonightHashSlow(null, 0));
}
[Fact]
public void Crytonote_Hash_Fast()
{
var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray();
var result = LibCryptonote.CryptonightHashFast(blobConverted).ToHexString();
Assert.Equal("ddc0e3a33b605ce39fa2d16a98d7634e33399ab1e4b56b3bdd3414b655fe9a98", result);
}
[Fact]
public void Cryptonote_FastHash_Should_Throw_On_Null_Argument()
{
Assert.Throws<ArgumentNullException>(() => LibCryptonote.CryptonightHashFast(null));
}
[Fact]
public void Crytonote_Hash_Slow_Lite()
{
var blobConverted = "0106f1adafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42597710c48c6d885e2622f40f82ecd9b9fd538f28df9b0557e07cd3237a31c76569ada98001".HexToByteArray();
var result = LibCryptonote.CryptonightHashSlowLite(blobConverted).ToHexString();
Assert.Equal("0769caee428a232cffb76fa200f174ff962734f24e7b3bf8d1b0d4e8ba6ceebf", result);
}
[Fact]
public void Crytonote_ConvertBlob()
{
var blob = "0106e5b3afd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b421c0300a401d90101ff9d0106d6d6a88702023c62e43372a58cb588147e20be53a27083f5c522f33c722b082ab7518c48cda280b4c4c32102609ec96e2499ee267d70efefc49f26e330526d3ef455314b7b5ba268a6045f8c80c0fc82aa0202fe5cc0fa56c4277d1a47827edce4725571529d57f33c73ada481ef84c323f30a8090cad2c60e02d88bf5e72a611c8b8464ce29e3b1adbfe1ae163886d9150fe511171cada98fcb80e08d84ddcb0102441915aaf9fbaf70ff454c701a6ae2bd59bb94dc0b888bf7e5d06274ee9238ca80c0caf384a302024078526e2132def44bde2806242652f5944e632f7d94290dd6ee5dda1929f5ee2b016e29f25f07ec2a8df59f0e118a6c9a4b769b745dc0c729071f6e0399d2585745020800000000012e7f76000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".HexToByteArray();
var result = LibCryptonote.ConvertBlob(blob, 330).ToHexString();
Assert.Equal("0106e5b3afd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b421c0300a4487286e262e95b8d2163a0c8b73527e8c9425adbdc4e532cf0ef4241f9ffbe9e01", result);
}
[Fact]
public void Cryptonote_ConvertBlob_Should_Throw_On_Null_Argument()
{
Assert.Throws<ArgumentNullException>(() => LibCryptonote.ConvertBlob(null, 0));
}
[Fact]
public void Crytonote_DecodeAddress()
{
var address = "48nhyWcSey31ngSEhV8j8NPm6B8PistCQJBjjDjmTvRSTWYg6iocAw131vE2JPh3ps33vgQDKLrUx3fcErusYWcMJBxpm1d";
var result = LibCryptonote.DecodeAddress(address);
Assert.Equal(18ul, result);
}
[Fact]
public void Cryptonote_DecodeAddress_Should_Throw_On_Null_Or_Empty_Argument()
{
Assert.Throws<ArgumentException>(() => LibCryptonote.DecodeAddress(null));
Assert.Throws<ArgumentException>(() => LibCryptonote.DecodeAddress(""));
}
[Fact]
public void Crytonote_DecodeIntegratedAddress()
{
var address = "4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQsGwTGg55Kq4p3ENE7";
var result = LibCryptonote.DecodeIntegratedAddress(address);
Assert.Equal(19ul, result);
}
}
}

View File

@ -18,7 +18,7 @@ namespace MiningCore.Tests.Crypto
private static readonly byte[] testValue2 = Enumerable.Repeat((byte)0x80, 80).ToArray();
[Fact]
public void Blake_Hash_Should_Match()
public void Blake_Hash()
{
var hasher = new Blake();
var result = hasher.Digest(testValue).ToHexString();
@ -34,7 +34,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Blake2s_Hash_Should_Match()
public void Blake2s_Hash()
{
var hasher = new Blake2s();
var result = hasher.Digest(testValue2).ToHexString();
@ -50,7 +50,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Groestl_Hash_Should_Match()
public void Groestl_Hash()
{
var hasher = new Groestl();
var result = hasher.Digest(testValue).ToHexString();
@ -66,7 +66,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Kezzak_Hash_Should_Match()
public void Kezzak_Hash()
{
var hasher = new Kezzak();
var result = hasher.Digest(testValue, 0ul).ToHexString();
@ -82,7 +82,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Scrypt_Hash_Should_Match()
public void Scrypt_Hash()
{
var hasher = new Scrypt(1024, 1);
var result = hasher.Digest(testValue).ToHexString();
@ -98,7 +98,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void NeoScrypt_Hash_Should_Match()
public void NeoScrypt_Hash()
{
var hasher = new NeoScrypt(0);
var result = hasher.Digest(testValue2).ToHexString();
@ -114,7 +114,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void ScryptN_Hash_Should_Match()
public void ScryptN_Hash()
{
var clock = new MockMasterClock { CurrentTime = new DateTime(2017, 10, 16) };
var hasher = new ScryptN(clock, new []{ Tuple.Create(2048L, 1389306217L) });
@ -132,7 +132,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Lyra2Rev2_Hash_Should_Match()
public void Lyra2Rev2_Hash()
{
var hasher = new Lyra2Rev2();
var result = hasher.Digest(Enumerable.Repeat((byte) 5, 80).ToArray()).ToHexString();
@ -155,7 +155,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Sha256D_Hash_Should_Match()
public void Sha256D_Hash()
{
var hasher = new Sha256D();
var result = hasher.Digest(testValue).ToHexString();
@ -171,7 +171,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Sha256S_Hash_Should_Match()
public void Sha256S_Hash()
{
var hasher = new Sha256S();
var result = hasher.Digest(testValue).ToHexString();
@ -187,7 +187,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void X11_Hash_Should_Match()
public void X11_Hash()
{
var hasher = new X11();
var result = hasher.Digest(testValue).ToHexString();
@ -203,7 +203,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void X17_Hash_Should_Match()
public void X17_Hash()
{
var hasher = new X17();
var result = hasher.Digest(testValue).ToHexString();
@ -219,7 +219,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Skein_Hash_Should_Match()
public void Skein_Hash()
{
var hasher = new Skein();
var result = hasher.Digest(testValue).ToHexString();
@ -235,7 +235,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Qubit_Hash_Should_Match()
public void Qubit_Hash()
{
var hasher = new Qubit();
var result = hasher.Digest(testValue).ToHexString();
@ -251,7 +251,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void GroestlMyriad_Hash_Should_Match()
public void GroestlMyriad_Hash()
{
var hasher = new GroestlMyriad();
var result = hasher.Digest(testValue).ToHexString();
@ -267,7 +267,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void DigestReverser_Hash_Should_Match()
public void DigestReverser_Hash()
{
var hasher = new DigestReverser(new Sha256S());
var result = hasher.Digest(testValue).ToHexString();
@ -317,7 +317,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Sha3_256_Hash_Should_Match()
public void Sha3_256_Hash()
{
var hasher = new Sha3_256();
var result = hasher.Digest(testValue).ToHexString();
@ -333,7 +333,7 @@ namespace MiningCore.Tests.Crypto
}
[Fact]
public void Sha3_512_Hash_Should_Match()
public void Sha3_512_Hash()
{
var hasher = new Sha3_512();
var result = hasher.Digest(testValue).ToHexString();

View File

@ -1,71 +0,0 @@
using System;
using System.Linq;
using System.Text;
using MiningCore.Extensions;
using MiningCore.Native;
using Xunit;
namespace MiningCore.Tests.Native
{
public class HashinLibCryptonoteTestsgTests : TestBase
{
private static readonly byte[] testValue = Enumerable.Repeat((byte)0x80, 128).ToArray();
[Fact]
public void Cryptonote_SlowHash_Should_Match()
{
var result = LibCryptonote.CryptonightHashSlow(testValue).ToHexString();
Assert.Equal("9a267e32aefcc40ab12a906fd3f2de45a24a5ccde9e8b84528e656577f14e0fe", result);
}
[Fact]
public void Cryptonote_SlowHash_Should_Throw_On_Null_Argument()
{
Assert.Throws<ArgumentNullException>(() => LibCryptonote.CryptonightHashSlow(null));
}
[Fact]
public void Cryptonote_FastHash_Should_Match()
{
var result = LibCryptonote.CryptonightHashFast(testValue).ToHexString();
Assert.Equal("80ad002b1c333a29913d9edb5340412b121c0e9045e59fa9b2aabb53f9dcc92d", result);
}
[Fact]
public void Cryptonote_FastHash_Should_Throw_On_Null_Argument()
{
Assert.Throws<ArgumentNullException>(() => LibCryptonote.CryptonightHashFast(null));
}
[Fact]
public void Cryptonote_DecodeAddress_Should_Match()
{
var result = LibCryptonote.DecodeAddress("9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8");
Assert.Equal(53u, result);
}
[Fact]
public void Cryptonote_DecodeAddress_Should_Throw_On_Null_Or_Empty_Argument()
{
Assert.Throws<ArgumentException>(() => LibCryptonote.DecodeAddress(null));
Assert.Throws<ArgumentException>(() => LibCryptonote.DecodeAddress(""));
}
[Fact]
public void Cryptonote_ConvertBlob_Should_Match()
{
var blob = "0105bfdfcecd05583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b420000000001d90101ff9d0106d6d6a88702020a79e36c5f5ac69abb68daa616b70e4dc911ed2edf50133fc121447cc403cd6780b4c4c32102b3adc5521c68a35e2dd1934e30b5fada872b384dbbf8c4e8130e43bd0097b8b680c0fc82aa0202b186f6745517ec23a87df7811849d71914a222c937da3e3a39c7bde6f27d2dc98090cad2c60e02df3a6eed49d05b0163986888ebe7da3fae808a72f3beec97346e0a18a960a7b180e08d84ddcb0102f37220a0c601e2dfe78cfab584cabeecf59079b3b2ee045561fb83ebf67941ba80c0caf384a30202b5e50c62333f3237d497eac37b26bd1217b6996eeb7d45e099b71b0f0b5399162b011c2515730ca7e8bb9b79e177557a1fa8b41e9aee544b25d69dc46f12f66b13f102080000000001ff0d7500".HexToByteArray();
var result = LibCryptonote.ConvertBlob(blob, blob.Length).ToHexString();
var expected = "0105bfdfcecd05583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b4200000000f262fa431f692fa1d8a6e89fb809487a2133dd6fd999d95c664b964df354ac4701";
Assert.Equal(expected, result);
}
[Fact]
public void Cryptonote_ConvertBlob_Should_Throw_On_Null_Argument()
{
Assert.Throws<ArgumentNullException>(() => LibCryptonote.ConvertBlob(null, 0));
}
}
}

View File

@ -34,9 +34,9 @@ namespace MiningCore
//////////////////////
// outgoing mappings
CreateMap<IShare, Share>();
CreateMap<Blockchain.Share, Persistence.Model.Share>();
CreateMap<IShare, Block>()
CreateMap<Blockchain.Share, Block>()
.ForMember(dest => dest.Reward, opt => opt.MapFrom(src => src.BlockReward))
.ForMember(dest => dest.Hash, opt => opt.MapFrom(src => src.BlockHash))
.ForMember(dest => dest.Status, opt => opt.Ignore());
@ -69,7 +69,7 @@ namespace MiningCore
CreateMap<WorkerPerformanceStatsContainer, Api.Responses.WorkerPerformanceStatsContainer>();
// PostgreSQL
CreateMap<Share, Persistence.Postgres.Entities.Share>();
CreateMap<Persistence.Model.Share, Persistence.Postgres.Entities.Share>();
CreateMap<Block, Persistence.Postgres.Entities.Block>();
CreateMap<Balance, Persistence.Postgres.Entities.Balance>();
CreateMap<Payment, Persistence.Postgres.Entities.Payment>();
@ -82,7 +82,7 @@ namespace MiningCore
// incoming mappings
// PostgreSQL
CreateMap<Persistence.Postgres.Entities.Share, Share>();
CreateMap<Persistence.Postgres.Entities.Share, Persistence.Model.Share>();
CreateMap<Persistence.Postgres.Entities.Block, Block>();
CreateMap<Persistence.Postgres.Entities.Balance, Balance>();
CreateMap<Persistence.Postgres.Entities.Payment, Payment>();

View File

@ -37,84 +37,4 @@ namespace MiningCore.Blockchain
{
string Next();
}
public interface IShare
{
/// <summary>
/// The pool originating this share from
/// </summary>
string PoolId { get; set; }
/// <summary>
/// Who mined it (wallet address)
/// </summary>
string Miner { get; }
/// <summary>
/// Who mined it
/// </summary>
string Worker { get; }
/// <summary>
/// Extra information for payout processing
/// </summary>
string PayoutInfo { get; set; }
/// <summary>
/// Mining Software
/// </summary>
string UserAgent { get; }
/// <summary>
/// From where was it submitted
/// </summary>
string IpAddress { get; }
/// <summary>
/// Submission source (pool, external stratum etc)
/// </summary>
string Source { get; set; }
/// <summary>
/// Stratum difficulty assigned to the miner at the time the share was submitted/accepted (used for payout
/// calculations)
/// </summary>
double Difficulty { get; set; }
/// <summary>
/// Block this share refers to
/// </summary>
long BlockHeight { get; set; }
/// <summary>
/// Block reward after deducting pool fee and donations
/// </summary>
decimal BlockReward { get; set; }
/// <summary>
/// Block hash
/// </summary>
string BlockHash { get; set; }
/// <summary>
/// If this share presumably resulted in a block
/// </summary>
bool IsBlockCandidate { get; set; }
/// <summary>
/// Arbitrary data to be interpreted by the payment processor specialized
/// in this coin to verify this block candidate was accepted by the network
/// </summary>
string TransactionConfirmationData { get; set; }
/// <summary>
/// Network difficulty at the time the share was submitted (used for some payout schemes like PPLNS)
/// </summary>
double NetworkDifficulty { get; set; }
/// <summary>
/// When the share was found
/// </summary>
DateTime Created { get; set; }
}
}

View File

@ -99,15 +99,12 @@ namespace MiningCore.Blockchain.Bitcoin
protected virtual void BuildCoinbase()
{
var extraNoncePlaceHolderLengthByte = (byte) extraNoncePlaceHolderLength;
// generate script parts
var sigScriptInitial = GenerateScriptSigInitial();
var sigScriptInitialBytes = sigScriptInitial.ToBytes();
var sigScriptLength = (uint) (
sigScriptInitial.Length +
1 + // for extranonce-placeholder length after sigScriptInitial
extraNoncePlaceHolderLength +
scriptSigFinalBytes.Length);
@ -138,9 +135,6 @@ namespace MiningCore.Blockchain.Bitcoin
bs.ReadWriteAsVarInt(ref sigScriptLength);
bs.ReadWrite(ref sigScriptInitialBytes);
// emit a simulated OP_PUSH(n) just without the payload (which is filled in by the miner: extranonce1 and extranonce2)
bs.ReadWrite(ref extraNoncePlaceHolderLengthByte);
// done
coinbaseInitial = stream.ToArray();
coinbaseInitialHex = coinbaseInitial.ToHexString();
@ -235,6 +229,9 @@ namespace MiningCore.Blockchain.Bitcoin
// push timestamp
ops.Add(Op.GetPushOp(now));
// push placeholder
ops.Add(Op.GetPushOp((uint) 0));
return new Script(ops);
}
@ -289,7 +286,7 @@ namespace MiningCore.Blockchain.Bitcoin
return blockHeader.ToBytes();
}
protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string extraNonce2, uint nTime, uint nonce)
protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string extraNonce2, uint nTime, uint nonce)
{
var context = worker.GetContextAs<BitcoinWorkerContext>();
var extraNonce1 = context.ExtraNonce1;
@ -330,24 +327,26 @@ namespace MiningCore.Blockchain.Bitcoin
throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
}
var result = new BitcoinShare
var result = new Share
{
BlockHeight = BlockTemplate.Height,
BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC),
NetworkDifficulty = Difficulty * shareMultiplier,
Difficulty = stratumDifficulty,
};
var blockBytes = SerializeBlock(headerBytes, coinbase);
if (isBlockCandidate)
{
result.IsBlockCandidate = true;
result.BlockHex = blockBytes.ToHexString();
result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC);
result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString();
var blockBytes = SerializeBlock(headerBytes, coinbase);
var blockHex = blockBytes.ToHexString();
return (result, blockHex);
}
return result;
return (result, null);
}
protected virtual byte[] SerializeCoinbase(string extraNonce1, string extraNonce2)
@ -479,7 +478,7 @@ namespace MiningCore.Blockchain.Bitcoin
return jobParams;
}
public virtual BitcoinShare ProcessShare(StratumClient worker,
public virtual (Share Share, string BlockHex) ProcessShare(StratumClient worker,
string extraNonce2, string nTime, string nonce)
{
Contract.RequiresNonNull(worker, nameof(worker));

View File

@ -20,6 +20,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reactive.Linq;
@ -269,23 +270,25 @@ namespace MiningCore.Blockchain.Bitcoin
BlockchainStats.ConnectedPeers = networkInfoResponse.Connections;
}
protected virtual async Task<(bool Accepted, string CoinbaseTransaction)> SubmitBlockAsync(BitcoinShare share)
protected virtual async Task<(bool Accepted, string CoinbaseTransaction)> SubmitBlockAsync(Share share, string blockHex)
{
// execute command batch
var results = await daemon.ExecuteBatchAnyAsync(
hasSubmitBlockMethod
? new DaemonCmd(BitcoinCommands.SubmitBlock, new[] { share.BlockHex })
: new DaemonCmd(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = share.BlockHex }),
? new DaemonCmd(BitcoinCommands.SubmitBlock, new[] { blockHex })
: new DaemonCmd(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = blockHex }),
new DaemonCmd(BitcoinCommands.GetBlock, new[] { share.BlockHash }));
// did submission succeed?
var submitResult = results[0];
var submitError = submitResult.Error?.Message ?? submitResult.Response?.ToString();
var submitError = submitResult.Error?.Message ??
submitResult.Error?.Code.ToString(CultureInfo.InvariantCulture) ??
submitResult.Response?.ToString();
if (!string.IsNullOrEmpty(submitError))
{
logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed with: {submitError}");
notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed with: {submitError}");
notificationService.NotifyAdmin($"[{share.PoolId.ToUpper()}]-[{share.Source}] Block submission failed", $"[{share.PoolId.ToUpper()}]-[{share.Source}] Block {share.BlockHeight} submission failed with: {submitError}");
return (false, null);
}
@ -298,7 +301,7 @@ namespace MiningCore.Blockchain.Bitcoin
if (!accepted)
{
logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission");
notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission");
notificationService.NotifyAdmin($"[{share.PoolId.ToUpper()}]-[{share.Source}] Block submission failed", $"[{share.PoolId.ToUpper()}]-[{share.Source}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission");
}
return (accepted, block?.Transactions.FirstOrDefault());
@ -445,7 +448,7 @@ namespace MiningCore.Blockchain.Bitcoin
return job.BlockTemplate.Transactions.Select(x => x.Data).ToArray();
}
public virtual async Task<BitcoinShare> SubmitShareAsync(StratumClient worker, object submission,
public virtual async Task<Share> SubmitShareAsync(StratumClient worker, object submission,
double stratumDifficultyBase)
{
Contract.RequiresNonNull(worker, nameof(worker));
@ -484,14 +487,23 @@ namespace MiningCore.Blockchain.Bitcoin
var workerName = split.Length > 1 ? split[1] : null;
// validate & process
var share = job.ProcessShare(worker, extraNonce2, nTime, nonce);
var (share, blockHex) = job.ProcessShare(worker, extraNonce2, nTime, nonce);
// enrich share with common data
share.PoolId = poolConfig.Id;
share.IpAddress = worker.RemoteEndpoint.Address.ToString();
share.Miner = minerName;
share.Worker = workerName;
share.UserAgent = context.UserAgent;
share.Source = clusterConfig.ClusterName;
share.Created = clock.Now;
// if block candidate, submit & check if accepted by network
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{share.BlockHash}]");
var acceptResponse = await SubmitBlockAsync(share);
var acceptResponse = await SubmitBlockAsync(share, blockHex);
// is it still a block candidate?
share.IsBlockCandidate = acceptResponse.Accepted;
@ -512,15 +524,6 @@ namespace MiningCore.Blockchain.Bitcoin
}
}
// enrich share with common data
share.PoolId = poolConfig.Id;
share.IpAddress = worker.RemoteEndpoint.Address.ToString();
share.Miner = minerName;
share.Worker = workerName;
share.UserAgent = context.UserAgent;
share.Source = clusterConfig.ClusterName;
share.Created = clock.Now;
return share;
}
@ -666,7 +669,8 @@ namespace MiningCore.Blockchain.Bitcoin
else
networkType = daemonInfoResponse.Testnet ? BitcoinNetworkType.Test : BitcoinNetworkType.Main;
ConfigureRewards();
if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
ConfigureRewards();
// update stats
BlockchainStats.NetworkType = networkType.ToString();

View File

@ -40,7 +40,7 @@ using NLog;
namespace MiningCore.Blockchain.Bitcoin
{
public class BitcoinPoolBase<TJob, TBlockTemplate> : PoolBase<BitcoinShare>
public class BitcoinPoolBase<TJob, TBlockTemplate> : PoolBase
where TBlockTemplate : BlockTemplate
where TJob : BitcoinJob<TBlockTemplate>, new()
{

View File

@ -44,6 +44,7 @@ namespace MiningCore.Blockchain.Bitcoin
private static readonly IHashAlgorithm qubit = new Qubit();
private static readonly IHashAlgorithm groestlMyriad = new GroestlMyriad();
private static readonly IHashAlgorithm neoScryptProfile1 = new NeoScrypt(0x80000620);
private static readonly IHashAlgorithm vergeBlockHasher = new DigestReverser(scrypt_1024_1);
private static readonly BitcoinCoinProperties sha256Coin =
new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse, "Sha256");
@ -79,16 +80,19 @@ namespace MiningCore.Blockchain.Bitcoin
new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, new DigestReverser(neoScryptProfile1), "Neoscrypt");
private static readonly BitcoinCoinProperties vergeLyraCoin =
new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(scrypt_1024_1), "Lyra2re2");
new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, vergeBlockHasher, "Lyra2re2");
private static readonly BitcoinCoinProperties vergeBlake2sCoin =
new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(scrypt_1024_1), "Blake2s");
new BitcoinCoinProperties(1, sha256D, blake2s, vergeBlockHasher, "Blake2s");
private static readonly BitcoinCoinProperties vergeX17Coin =
new BitcoinCoinProperties(1, x17, blake2s, new DigestReverser(scrypt_1024_1), "X17");
new BitcoinCoinProperties(1, sha256D, x17, vergeBlockHasher, "X17");
private static readonly BitcoinCoinProperties vergeScryptCoin =
new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, vergeBlockHasher, "Scrypt");
private static readonly BitcoinCoinProperties vergeGroestlCoin =
new BitcoinCoinProperties(1, groestlMyriad, blake2s, new DigestReverser(scrypt_1024_1), "Groestl-Myriad");
new BitcoinCoinProperties(1, sha256D, groestlMyriad, vergeBlockHasher, "Groestl-Myriad");
private static readonly Dictionary<CoinType, BitcoinCoinProperties> coinProperties = new Dictionary<CoinType, BitcoinCoinProperties>
{
@ -97,7 +101,7 @@ namespace MiningCore.Blockchain.Bitcoin
{ CoinType.BCH, sha256Coin },
{ CoinType.NMC, sha256Coin },
{ CoinType.PPC, sha256Coin },
{ CoinType.GLT, sha256Coin },
{ CoinType.GLT, sha256Coin },
// Scrypt
{ CoinType.LTC, scryptCoin },
@ -145,9 +149,10 @@ namespace MiningCore.Blockchain.Bitcoin
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty");
switch(algorithm.ToLower())
switch (algorithm.ToLower())
{
case "sha256d":
case "sha256":
return sha256Coin;
case "skein":
@ -184,7 +189,7 @@ namespace MiningCore.Blockchain.Bitcoin
return vergeBlake2sCoin;
default: // scrypt
return scryptCoin;
return vergeScryptCoin;
}
}

View File

@ -1,27 +0,0 @@
/*
Copyright 2017 Coin Foundry (coinfoundry.org)
Authors: Oliver Weichhold (oliver@weichhold.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace MiningCore.Blockchain.Bitcoin
{
public class BitcoinShare : ShareBase
{
public string BlockHex { get; set; }
}
}

View File

@ -1,150 +1,154 @@
using System;
using System.Collections.Generic;
using MiningCore.Blockchain.Bitcoin;
using MiningCore.Blockchain.Ethereum;
using MiningCore.Configuration;
namespace MiningCore.Blockchain
{
public static class CoinMetaData
{
public const string BlockHeightPH = "$height$";
public const string BlockHashPH = "$hash$";
public static readonly Dictionary<CoinType, Dictionary<string, string>> BlockInfoLinks = new Dictionary<CoinType, Dictionary<string, string>>
{
{ CoinType.ETH, new Dictionary<string, string>
{
{ string.Empty, $"https://etherscan.io/block/{BlockHeightPH}" },
{ EthereumConstants.BlockTypeUncle, $"https://etherscan.io/uncle/{BlockHeightPH}" },
}},
{ CoinType.ETC, new Dictionary<string, string>
{
{ string.Empty, $"https://gastracker.io/block/{BlockHeightPH}" },
{ EthereumConstants.BlockTypeUncle, $"https://gastracker.io/uncle/{BlockHeightPH}" }
}},
{ CoinType.XMR, new Dictionary<string, string> { { string.Empty, $"https://chainradar.com/xmr/block/{BlockHeightPH}" }}},
{ CoinType.ETN, new Dictionary<string, string> { { string.Empty, $"https://blockexplorer.electroneum.com/block/{BlockHeightPH}" } }},
{ CoinType.LTC, new Dictionary<string, string> { { string.Empty, $"https://chainz.cryptoid.info/ltc/block.dws?{BlockHeightPH}.htm" } }},
{ CoinType.BCH, new Dictionary<string, string> { { string.Empty, $"https://www.blocktrail.com/BCC/block/{BlockHeightPH}" }}},
{ CoinType.DASH, new Dictionary<string, string> { { string.Empty, $"https://chainz.cryptoid.info/dash/block.dws?{BlockHeightPH}.htm" }}},
{ CoinType.BTC, new Dictionary<string, string> { { string.Empty, $"https://blockchain.info/block/{BlockHeightPH}" }}},
{ CoinType.DOGE, new Dictionary<string, string> { { string.Empty, $"https://dogechain.info/block/{BlockHeightPH}" }}},
{ CoinType.ZEC, new Dictionary<string, string> { { string.Empty, $"https://explorer.zcha.in/blocks/{BlockHashPH}" } }},
{ CoinType.BTCP, new Dictionary<string, string> { { string.Empty, $"http://explorer.btcprivate.org/block/{BlockHashPH}" } }},
{ CoinType.ZCL, new Dictionary<string, string> { { string.Empty, $"http://explorer.zclmine.pro/block/{BlockHeightPH}" }}},
{ CoinType.ZEN, new Dictionary<string, string> { { string.Empty, $"http://explorer.zensystem.io/block/{BlockHashPH}" } }},
{ CoinType.DGB, new Dictionary<string, string> { { string.Empty, $"https://digiexplorer.info/block/{BlockHeightPH}" }}},
{ CoinType.NMC, new Dictionary<string, string> { { string.Empty, $"https://explorer.namecoin.info/b/{BlockHeightPH}" }}},
{ CoinType.GRS, new Dictionary<string, string> { { string.Empty, $"https://groestlsight.groestlcoin.org/block/{BlockHeightPH}" }}},
{ CoinType.MONA, new Dictionary<string, string> { { string.Empty, $"https://bchain.info/MONA/block/{BlockHeightPH}" }}},
{ CoinType.GLT, new Dictionary<string, string> { { string.Empty, $"https://bchain.info/GLT/block/{BlockHeightPH}" }}},
{ CoinType.VTC, new Dictionary<string, string> { { string.Empty, $"https://bchain.info/VTC/block/{BlockHeightPH}" }}},
{ CoinType.BTG, new Dictionary<string, string> { { string.Empty, $"https://btg-bitcore2.trezor.io/block/{BlockHashPH}" } }},
{ CoinType.ELLA, new Dictionary<string, string> { { string.Empty, $"https://explorer.ellaism.org/block/{BlockHeightPH}" }}},
{ CoinType.EXP, new Dictionary<string, string> { { string.Empty, $"http://www.gander.tech/blocks/{BlockHeightPH}" }}},
{ CoinType.AEON, new Dictionary<string, string> { { string.Empty, $"https://chainradar.com/aeon/block/{BlockHeightPH}" }}},
{ CoinType.STAK, new Dictionary<string, string> { { string.Empty, $"https://straks.info/block/{BlockHeightPH}" }}},
{ CoinType.MOON, new Dictionary<string, string> { { string.Empty, $"https://chainz.cryptoid.info/moon/block.dws?{BlockHeightPH}.htm" }}},
{ CoinType.XVG, new Dictionary<string, string> { { string.Empty, $"https://verge-blockchain.info/block/{BlockHashPH}" } }},
};
public static readonly Dictionary<CoinType, string> TxInfoLinks = new Dictionary<CoinType, string>
{
{ CoinType.XMR, "https://chainradar.com/xmr/transaction/{0}" },
{ CoinType.ETN, "https://blockexplorer.electroneum.com/tx/{0}" },
{ CoinType.ETH, "https://etherscan.io/tx/{0}" },
{ CoinType.ETC, "https://gastracker.io/tx/{0}" },
{ CoinType.LTC, "https://chainz.cryptoid.info/ltc/tx.dws?{0}.htm" },
{ CoinType.BCH, "https://www.blocktrail.com/BCC/tx/{0}" },
{ CoinType.DASH, "https://chainz.cryptoid.info/dash/tx.dws?{0}.htm" },
{ CoinType.BTC, "https://blockchain.info/tx/{0}" },
{ CoinType.DOGE, "https://dogechain.info/tx/{0}" },
{ CoinType.ZEC, "https://explorer.zcha.in/transactions/{0}" },
{ CoinType.ZCL, "http://explorer.zclmine.pro/transactions/{0}" },
{ CoinType.ZEN, "http://explorer.zensystem.io/transactions/{0}" },
{ CoinType.BTCP, "https://explorer.btcprivate.org/transactions/{0}" },
{ CoinType.DGB, "https://digiexplorer.info/tx/{0}" },
{ CoinType.NMC, "https://explorer.namecoin.info/tx/{0}" },
{ CoinType.GRS, "https://groestlsight.groestlcoin.org/tx/{0}" },
{ CoinType.MONA, "https://bchain.info/MONA/tx/{0}" },
{ CoinType.STAK, "https://straks.info/transaction/{0}" },
{ CoinType.GLT, "https://bchain.info/GLT/tx/{0}" },
{ CoinType.VTC, "https://bchain.info/VTC/tx/{0}" },
{ CoinType.BTG, "https://btgexp.com/tx/{0}" },
{ CoinType.ELLA, "https://explorer.ellaism.org/tx/{0}" },
{ CoinType.EXP, "http://www.gander.tech/tx/{0}" },
{ CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" },
{ CoinType.MOON, "https://chainz.cryptoid.info/moon/tx.dws?{0}.htm" },
{ CoinType.XVG, "https://verge-blockchain.info/tx/{0}" },
{ CoinType.GBX, "http://gobyte.ezmine.io/tx/{0}" },
{ CoinType.CRC, "http://explorer.cryptopros.us/tx/{0}" },
};
public static readonly Dictionary<CoinType, string> AddressInfoLinks = new Dictionary<CoinType, string>
{
{ CoinType.ETH, "https://etherscan.io/address/{0}" },
{ CoinType.ETC, "https://gastracker.io/addr/{0}" },
{ CoinType.LTC, "https://chainz.cryptoid.info/ltc/address.dws?{0}.htm" },
{ CoinType.BCH, "https://www.blocktrail.com/BCC/address/{0}" },
{ CoinType.DASH, "https://chainz.cryptoid.info/dash/address.dws?{0}.htm" },
{ CoinType.BTC, "https://blockchain.info/address/{0}" },
{ CoinType.DOGE, "https://dogechain.info/address/{0}" },
{ CoinType.ZEC, "https://explorer.zcha.in/accounts/{0}" },
{ CoinType.ZCL, "http://explorer.zclmine.pro/accounts/{0}" },
{ CoinType.ZEN, "http://explorer.zensystem.io/accounts/{0}" },
{ CoinType.DGB, "https://digiexplorer.info/address/{0}" },
{ CoinType.NMC, "https://explorer.namecoin.info/a/{0}" },
{ CoinType.GRS, "https://groestlsight.groestlcoin.org/address/{0}" },
{ CoinType.MONA, "https://bchain.info/MONA/addr/{0}" },
{ CoinType.STAK, "https://straks.info/address/{0}" },
{ CoinType.GLT, "https://bchain.info/GLT/addr/{0}" },
{ CoinType.VTC, "https://bchain.info/VTC/addr/{0}" },
{ CoinType.BTG, "https://btgexp.com/address/{0}" },
{ CoinType.ELLA, "https://explorer.ellaism.org/addr/{0}" },
{ CoinType.EXP, "http://www.gander.tech/address/{0}" },
{ CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" },
{ CoinType.XVG, "https://verge-blockchain.info/address/{0}" },
{ CoinType.GBX, "http://gobyte.ezmine.io/address/{0}" },
{ CoinType.CRC, "http://explorer.cryptopros.us/address/{0}" },
};
private const string Ethash = "Dagger-Hashimoto";
private const string Cryptonight = "Cryptonight";
private const string CryptonightLight = "Cryptonight-Light";
public static readonly Dictionary<CoinType, Func<CoinType, string, string>> CoinAlgorithm = new Dictionary<CoinType, Func<CoinType, string, string>>
{
{ CoinType.ETH, (coin, alg)=> Ethash },
{ CoinType.ETC, (coin, alg)=> Ethash },
{ CoinType.LTC, BitcoinProperties.GetAlgorithm },
{ CoinType.BCH, BitcoinProperties.GetAlgorithm },
{ CoinType.DASH, BitcoinProperties.GetAlgorithm },
{ CoinType.BTC, BitcoinProperties.GetAlgorithm },
{ CoinType.DOGE, BitcoinProperties.GetAlgorithm },
{ CoinType.ZEC, BitcoinProperties.GetAlgorithm },
{ CoinType.ZCL, BitcoinProperties.GetAlgorithm },
{ CoinType.BTCP, BitcoinProperties.GetAlgorithm },
{ CoinType.ZEN, BitcoinProperties.GetAlgorithm },
{ CoinType.DGB, BitcoinProperties.GetAlgorithm },
{ CoinType.NMC, BitcoinProperties.GetAlgorithm },
{ CoinType.GRS, BitcoinProperties.GetAlgorithm },
{ CoinType.MONA, BitcoinProperties.GetAlgorithm },
{ CoinType.STAK, BitcoinProperties.GetAlgorithm },
{ CoinType.GLT, BitcoinProperties.GetAlgorithm },
{ CoinType.VTC, BitcoinProperties.GetAlgorithm },
{ CoinType.BTG, BitcoinProperties.GetAlgorithm },
{ CoinType.ELLA, (coin, alg)=> Ethash },
{ CoinType.EXP, (coin, alg)=> Ethash },
{ CoinType.MOON, BitcoinProperties.GetAlgorithm },
{ CoinType.XVG, BitcoinProperties.GetAlgorithm },
{ CoinType.XMR, (coin, alg)=> Cryptonight },
{ CoinType.ETN, (coin, alg)=> Cryptonight },
{ CoinType.AEON, (coin, alg)=> CryptonightLight },
{ CoinType.GBX, BitcoinProperties.GetAlgorithm },
{ CoinType.CRC, BitcoinProperties.GetAlgorithm },
};
}
}
using System;
using System.Collections.Generic;
using MiningCore.Blockchain.Bitcoin;
using MiningCore.Blockchain.Ethereum;
using MiningCore.Configuration;
namespace MiningCore.Blockchain
{
public static class CoinMetaData
{
public const string BlockHeightPH = "$height$";
public const string BlockHashPH = "$hash$";
public static readonly Dictionary<CoinType, Dictionary<string, string>> BlockInfoLinks = new Dictionary<CoinType, Dictionary<string, string>>
{
{ CoinType.ETH, new Dictionary<string, string>
{
{ string.Empty, $"https://etherscan.io/block/{BlockHeightPH}" },
{ EthereumConstants.BlockTypeUncle, $"https://etherscan.io/uncle/{BlockHeightPH}" },
}},
{ CoinType.ETC, new Dictionary<string, string>
{
{ string.Empty, $"https://gastracker.io/block/{BlockHeightPH}" },
{ EthereumConstants.BlockTypeUncle, $"https://gastracker.io/uncle/{BlockHeightPH}" }
}},
{ CoinType.XMR, new Dictionary<string, string> { { string.Empty, $"https://chainradar.com/xmr/block/{BlockHeightPH}" }}},
{ CoinType.ETN, new Dictionary<string, string> { { string.Empty, $"https://blockexplorer.electroneum.com/block/{BlockHeightPH}" } }},
{ CoinType.LTC, new Dictionary<string, string> { { string.Empty, $"https://chainz.cryptoid.info/ltc/block.dws?{BlockHeightPH}.htm" } }},
{ CoinType.PPC, new Dictionary<string, string> { { string.Empty, $"https://chainz.cryptoid.info/ppc/block.dws?{BlockHeightPH}.htm" } }},
{ CoinType.BCH, new Dictionary<string, string> { { string.Empty, $"https://www.blocktrail.com/BCC/block/{BlockHeightPH}" }}},
{ CoinType.DASH, new Dictionary<string, string> { { string.Empty, $"https://chainz.cryptoid.info/dash/block.dws?{BlockHeightPH}.htm" }}},
{ CoinType.BTC, new Dictionary<string, string> { { string.Empty, $"https://blockchain.info/block/{BlockHeightPH}" }}},
{ CoinType.DOGE, new Dictionary<string, string> { { string.Empty, $"https://dogechain.info/block/{BlockHeightPH}" }}},
{ CoinType.ZEC, new Dictionary<string, string> { { string.Empty, $"https://explorer.zcha.in/blocks/{BlockHashPH}" } }},
{ CoinType.BTCP, new Dictionary<string, string> { { string.Empty, $"http://explorer.btcprivate.org/block/{BlockHashPH}" } }},
{ CoinType.ZCL, new Dictionary<string, string> { { string.Empty, $"http://explorer.zclmine.pro/block/{BlockHeightPH}" }}},
{ CoinType.ZEN, new Dictionary<string, string> { { string.Empty, $"http://explorer.zensystem.io/block/{BlockHashPH}" } }},
{ CoinType.DGB, new Dictionary<string, string> { { string.Empty, $"https://digiexplorer.info/block/{BlockHeightPH}" }}},
{ CoinType.NMC, new Dictionary<string, string> { { string.Empty, $"https://explorer.namecoin.info/b/{BlockHeightPH}" }}},
{ CoinType.GRS, new Dictionary<string, string> { { string.Empty, $"https://groestlsight.groestlcoin.org/block/{BlockHeightPH}" }}},
{ CoinType.MONA, new Dictionary<string, string> { { string.Empty, $"https://bchain.info/MONA/block/{BlockHeightPH}" }}},
{ CoinType.GLT, new Dictionary<string, string> { { string.Empty, $"https://bchain.info/GLT/block/{BlockHeightPH}" }}},
{ CoinType.VTC, new Dictionary<string, string> { { string.Empty, $"https://bchain.info/VTC/block/{BlockHeightPH}" }}},
{ CoinType.BTG, new Dictionary<string, string> { { string.Empty, $"https://btg-bitcore2.trezor.io/block/{BlockHashPH}" } }},
{ CoinType.ELLA, new Dictionary<string, string> { { string.Empty, $"https://explorer.ellaism.org/block/{BlockHeightPH}" }}},
{ CoinType.EXP, new Dictionary<string, string> { { string.Empty, $"http://www.gander.tech/blocks/{BlockHeightPH}" }}},
{ CoinType.AEON, new Dictionary<string, string> { { string.Empty, $"https://chainradar.com/aeon/block/{BlockHeightPH}" }}},
{ CoinType.STAK, new Dictionary<string, string> { { string.Empty, $"https://straks.info/block/{BlockHeightPH}" }}},
{ CoinType.MOON, new Dictionary<string, string> { { string.Empty, $"https://chainz.cryptoid.info/moon/block.dws?{BlockHeightPH}.htm" }}},
{ CoinType.XVG, new Dictionary<string, string> { { string.Empty, $"https://verge-blockchain.info/block/{BlockHashPH}" } }},
};
public static readonly Dictionary<CoinType, string> TxInfoLinks = new Dictionary<CoinType, string>
{
{ CoinType.XMR, "https://chainradar.com/xmr/transaction/{0}" },
{ CoinType.ETN, "https://blockexplorer.electroneum.com/tx/{0}" },
{ CoinType.ETH, "https://etherscan.io/tx/{0}" },
{ CoinType.ETC, "https://gastracker.io/tx/{0}" },
{ CoinType.LTC, "https://chainz.cryptoid.info/ltc/tx.dws?{0}.htm" },
{ CoinType.PPC, "https://chainz.cryptoid.info/ppc/tx.dws?{0}.htm" },
{ CoinType.BCH, "https://www.blocktrail.com/BCC/tx/{0}" },
{ CoinType.DASH, "https://chainz.cryptoid.info/dash/tx.dws?{0}.htm" },
{ CoinType.BTC, "https://blockchain.info/tx/{0}" },
{ CoinType.DOGE, "https://dogechain.info/tx/{0}" },
{ CoinType.ZEC, "https://explorer.zcha.in/transactions/{0}" },
{ CoinType.ZCL, "http://explorer.zclmine.pro/transactions/{0}" },
{ CoinType.ZEN, "http://explorer.zensystem.io/transactions/{0}" },
{ CoinType.BTCP, "https://explorer.btcprivate.org/transactions/{0}" },
{ CoinType.DGB, "https://digiexplorer.info/tx/{0}" },
{ CoinType.NMC, "https://explorer.namecoin.info/tx/{0}" },
{ CoinType.GRS, "https://groestlsight.groestlcoin.org/tx/{0}" },
{ CoinType.MONA, "https://bchain.info/MONA/tx/{0}" },
{ CoinType.STAK, "https://straks.info/transaction/{0}" },
{ CoinType.GLT, "https://bchain.info/GLT/tx/{0}" },
{ CoinType.VTC, "https://bchain.info/VTC/tx/{0}" },
{ CoinType.BTG, "https://btgexp.com/tx/{0}" },
{ CoinType.ELLA, "https://explorer.ellaism.org/tx/{0}" },
{ CoinType.EXP, "http://www.gander.tech/tx/{0}" },
{ CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" },
{ CoinType.MOON, "https://chainz.cryptoid.info/moon/tx.dws?{0}.htm" },
{ CoinType.XVG, "https://verge-blockchain.info/tx/{0}" },
{ CoinType.GBX, "http://gobyte.ezmine.io/tx/{0}" },
{ CoinType.CRC, "http://explorer.cryptopros.us/tx/{0}" },
};
public static readonly Dictionary<CoinType, string> AddressInfoLinks = new Dictionary<CoinType, string>
{
{ CoinType.ETH, "https://etherscan.io/address/{0}" },
{ CoinType.ETC, "https://gastracker.io/addr/{0}" },
{ CoinType.LTC, "https://chainz.cryptoid.info/ltc/address.dws?{0}.htm" },
{ CoinType.PPC, "https://chainz.cryptoid.info/ppc/address.dws?{0}.htm" },
{ CoinType.BCH, "https://www.blocktrail.com/BCC/address/{0}" },
{ CoinType.DASH, "https://chainz.cryptoid.info/dash/address.dws?{0}.htm" },
{ CoinType.BTC, "https://blockchain.info/address/{0}" },
{ CoinType.DOGE, "https://dogechain.info/address/{0}" },
{ CoinType.ZEC, "https://explorer.zcha.in/accounts/{0}" },
{ CoinType.ZCL, "http://explorer.zclmine.pro/accounts/{0}" },
{ CoinType.ZEN, "http://explorer.zensystem.io/accounts/{0}" },
{ CoinType.DGB, "https://digiexplorer.info/address/{0}" },
{ CoinType.NMC, "https://explorer.namecoin.info/a/{0}" },
{ CoinType.GRS, "https://groestlsight.groestlcoin.org/address/{0}" },
{ CoinType.MONA, "https://bchain.info/MONA/addr/{0}" },
{ CoinType.STAK, "https://straks.info/address/{0}" },
{ CoinType.GLT, "https://bchain.info/GLT/addr/{0}" },
{ CoinType.VTC, "https://bchain.info/VTC/addr/{0}" },
{ CoinType.BTG, "https://btgexp.com/address/{0}" },
{ CoinType.ELLA, "https://explorer.ellaism.org/addr/{0}" },
{ CoinType.EXP, "http://www.gander.tech/address/{0}" },
{ CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" },
{ CoinType.XVG, "https://verge-blockchain.info/address/{0}" },
{ CoinType.GBX, "http://gobyte.ezmine.io/address/{0}" },
{ CoinType.CRC, "http://explorer.cryptopros.us/address/{0}" },
};
private const string Ethash = "Dagger-Hashimoto";
private const string Cryptonight = "Cryptonight";
private const string CryptonightLight = "Cryptonight-Light";
public static readonly Dictionary<CoinType, Func<CoinType, string, string>> CoinAlgorithm = new Dictionary<CoinType, Func<CoinType, string, string>>
{
{ CoinType.ETH, (coin, alg)=> Ethash },
{ CoinType.ETC, (coin, alg)=> Ethash },
{ CoinType.LTC, BitcoinProperties.GetAlgorithm },
{ CoinType.PPC, BitcoinProperties.GetAlgorithm },
{ CoinType.BCH, BitcoinProperties.GetAlgorithm },
{ CoinType.DASH, BitcoinProperties.GetAlgorithm },
{ CoinType.BTC, BitcoinProperties.GetAlgorithm },
{ CoinType.DOGE, BitcoinProperties.GetAlgorithm },
{ CoinType.ZEC, BitcoinProperties.GetAlgorithm },
{ CoinType.ZCL, BitcoinProperties.GetAlgorithm },
{ CoinType.BTCP, BitcoinProperties.GetAlgorithm },
{ CoinType.ZEN, BitcoinProperties.GetAlgorithm },
{ CoinType.DGB, BitcoinProperties.GetAlgorithm },
{ CoinType.NMC, BitcoinProperties.GetAlgorithm },
{ CoinType.GRS, BitcoinProperties.GetAlgorithm },
{ CoinType.MONA, BitcoinProperties.GetAlgorithm },
{ CoinType.STAK, BitcoinProperties.GetAlgorithm },
{ CoinType.GLT, BitcoinProperties.GetAlgorithm },
{ CoinType.VTC, BitcoinProperties.GetAlgorithm },
{ CoinType.BTG, BitcoinProperties.GetAlgorithm },
{ CoinType.ELLA, (coin, alg)=> Ethash },
{ CoinType.EXP, (coin, alg)=> Ethash },
{ CoinType.MOON, BitcoinProperties.GetAlgorithm },
{ CoinType.XVG, BitcoinProperties.GetAlgorithm },
{ CoinType.XMR, (coin, alg)=> Cryptonight },
{ CoinType.ETN, (coin, alg)=> Cryptonight },
{ CoinType.AEON, (coin, alg)=> CryptonightLight },
{ CoinType.GBX, BitcoinProperties.GetAlgorithm },
{ CoinType.CRC, BitcoinProperties.GetAlgorithm },
};
}
}

View File

@ -49,7 +49,7 @@ namespace MiningCore.Blockchain.Ethereum
}
}
public async Task<EthereumShare> ProcessShareAsync(StratumClient worker, string nonce, EthashFull ethash)
public async Task<(Share Share, string FullNonceHex, string HeaderHash, string MixHash)> ProcessShareAsync(StratumClient worker, string nonce, EthashFull ethash)
{
// duplicate nonce?
lock(workerNonces)
@ -98,25 +98,30 @@ namespace MiningCore.Blockchain.Ethereum
}
// create share
var share = new EthereumShare
var share = new Share
{
BlockHeight = (long) BlockTemplate.Height,
IpAddress = worker.RemoteEndpoint?.Address?.ToString(),
Miner = context.MinerName,
Worker = context.WorkerName,
UserAgent = context.UserAgent,
FullNonceHex = "0x" + fullNonceHex,
HeaderHash = BlockTemplate.Header,
MixHash = mixDigest.ToHexString(true),
IsBlockCandidate = isBlockCandidate,
Difficulty = stratumDifficulty * EthereumConstants.Pow2x32,
BlockHash = mixDigest.ToHexString(true) // OW: is this correct?
};
if (share.IsBlockCandidate)
share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{share.FullNonceHex}";
{
fullNonceHex = "0x" + fullNonceHex;
var headerHash = BlockTemplate.Header;
var mixHash = mixDigest.ToHexString(true);
return share;
share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{fullNonceHex}";
return (share, fullNonceHex, headerHash, mixHash);
}
return (share, null, null, null);
}
}
}

View File

@ -291,14 +291,14 @@ namespace MiningCore.Blockchain.Ethereum
BlockchainStats.ConnectedPeers = peerCount;
}
private async Task<bool> SubmitBlockAsync(EthereumShare share)
private async Task<bool> SubmitBlockAsync(Share share, string fullNonceHex, string headerHash, string mixHash)
{
// submit work
var response = await daemon.ExecuteCmdAnyAsync<object>(EC.SubmitWork, new[]
{
share.FullNonceHex,
share.HeaderHash,
share.MixHash
fullNonceHex,
headerHash,
mixHash
});
if (response.Error != null || (bool?) response.Response == false)
@ -393,7 +393,7 @@ namespace MiningCore.Blockchain.Ethereum
context.ExtraNonce1 = extraNonceProvider.Next();
}
public async Task<EthereumShare> SubmitShareAsync(StratumClient worker,
public async Task<Share> SubmitShareAsync(StratumClient worker,
string[] request, double stratumDifficulty, double stratumDifficultyBase)
{
Contract.RequiresNonNull(worker, nameof(worker));
@ -415,20 +415,7 @@ namespace MiningCore.Blockchain.Ethereum
}
// validate & process
var share = await job.ProcessShareAsync(worker, nonce, ethash);
// if block candidate, submit & check if accepted by network
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight}");
share.IsBlockCandidate = await SubmitBlockAsync(share);
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}");
}
}
var (share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, nonce, ethash);
// enrich share with common data
share.PoolId = poolConfig.Id;
@ -436,6 +423,19 @@ namespace MiningCore.Blockchain.Ethereum
share.Source = clusterConfig.ClusterName;
share.Created = clock.Now;
// if block candidate, submit & check if accepted by network
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight}");
share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, mixHash);
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}");
}
}
return share;
}
@ -542,7 +542,8 @@ namespace MiningCore.Blockchain.Ethereum
EthereumUtils.DetectNetworkAndChain(netVersion, parityChain, out networkType, out chainType);
ConfigureRewards();
if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
ConfigureRewards();
// update stats
BlockchainStats.RewardType = "POW";

View File

@ -42,7 +42,7 @@ using Newtonsoft.Json;
namespace MiningCore.Blockchain.Ethereum
{
[CoinMetadata(CoinType.ETH, CoinType.ETC, CoinType.EXP, CoinType.ELLA)]
public class EthereumPool : PoolBase<EthereumShare>
public class EthereumPool : PoolBase
{
public EthereumPool(IComponentContext ctx,
JsonSerializerSettings serializerSettings,

View File

@ -1,29 +0,0 @@
/*
Copyright 2017 Coin Foundry (coinfoundry.org)
Authors: Oliver Weichhold (oliver@weichhold.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace MiningCore.Blockchain.Ethereum
{
public class EthereumShare : ShareBase
{
public string FullNonceHex { get; set; }
public string HeaderHash { get; set; }
public string MixHash { get; set; }
}
}

View File

@ -19,10 +19,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Buffers;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System;
using MiningCore.Blockchain.Monero.DaemonResponses;
using MiningCore.Buffers;
using MiningCore.Configuration;
@ -49,11 +46,15 @@ namespace MiningCore.Blockchain.Monero
switch (poolConfig.Coin.Type)
{
case CoinType.AEON:
hashSlow = LibCryptonote.CryptonightHashSlowLite;
hashSlow = (buf, variant)=> LibCryptonote.CryptonightHashSlowLite(buf);
break;
default:
hashSlow = LibCryptonote.CryptonightHashSlow;
case CoinType.XMR:
hashSlow = LibCryptonote.CryptonightHashSlow;
break;
default:
hashSlow = (buf, variant) => LibCryptonote.CryptonightHashSlow(buf, 0);
break;
}
@ -61,7 +62,7 @@ namespace MiningCore.Blockchain.Monero
PrepareBlobTemplate(instanceId);
}
private readonly Func<byte[], PooledArraySegment<byte>> hashSlow;
private readonly Func<byte[], int, PooledArraySegment<byte>> hashSlow;
private byte[] blobTemplate;
private uint extraNonce;
@ -135,7 +136,7 @@ namespace MiningCore.Blockchain.Monero
target = EncodeTarget(workerJob.Difficulty);
}
public MoneroShare ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient worker)
public (Share Share, string BlobHex, string BlobHash) ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient worker)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty");
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(workerHash), $"{nameof(workerHash)} must not be empty");
@ -165,8 +166,11 @@ namespace MiningCore.Blockchain.Monero
if (blobConverted == null)
throw new StratumException(StratumError.MinusOne, "malformed blob");
// hash it
using (var hashSeg = hashSlow(blobConverted))
// PoW variant
var hashVariant = blobConverted[0] >= 7 ? blobConverted[0] - 6 : 0;
// hash it
using (var hashSeg = hashSlow(blobConverted, hashVariant))
{
var hash = hashSeg.ToHexString();
if (hash != workerHash)
@ -200,17 +204,18 @@ namespace MiningCore.Blockchain.Monero
using (var blockHash = ComputeBlockHash(blobConverted))
{
var result = new MoneroShare
var result = new Share
{
BlockHeight = BlockTemplate.Height,
IsBlockCandidate = isBlockCandidate,
BlobHex = blob.ToHexString(),
BlobHash = blockHash.ToHexString(),
BlockHash = blockHash.ToHexString(),
Difficulty = stratumDifficulty,
};
return result;
var blobHex = blob.ToHexString();
var blobHash = blockHash.ToHexString();
return (result, blobHex, blobHash);
}
}
}

View File

@ -169,15 +169,15 @@ namespace MiningCore.Blockchain.Monero
BlockchainStats.ConnectedPeers = info.OutgoingConnectionsCount + info.IncomingConnectionsCount;
}
private async Task<bool> SubmitBlockAsync(MoneroShare share)
private async Task<bool> SubmitBlockAsync(Share share, string blobHex, string blobHash)
{
var response = await daemon.ExecuteCmdAnyAsync<SubmitResponse>(MC.SubmitBlock, new[] { share.BlobHex });
var response = await daemon.ExecuteCmdAnyAsync<SubmitResponse>(MC.SubmitBlock, new[] { blobHex });
if (response.Error != null || response?.Response?.Status != "OK")
{
var error = response.Error?.Message ?? response.Response?.Status;
logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}] submission failed with: {error}");
logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} [{blobHash.Substring(0, 6)}] submission failed with: {error}");
notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed with: {error}");
return false;
@ -204,13 +204,16 @@ namespace MiningCore.Blockchain.Monero
.Where(x => string.IsNullOrEmpty(x.Category))
.ToArray();
// extract wallet daemon endpoints
walletDaemonEndpoints = poolConfig.Daemons
.Where(x => x.Category?.ToLower() == MoneroConstants.WalletDaemonCategory)
.ToArray();
if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
{
// extract wallet daemon endpoints
walletDaemonEndpoints = poolConfig.Daemons
.Where(x => x.Category?.ToLower() == MoneroConstants.WalletDaemonCategory)
.ToArray();
if (walletDaemonEndpoints.Length == 0)
logger.ThrowLogPoolStartupException("Wallet-RPC daemon is not configured (Daemon configuration for monero-pools require an additional entry of category \'wallet' pointing to the wallet daemon)", LogCat);
if (walletDaemonEndpoints.Length == 0)
logger.ThrowLogPoolStartupException("Wallet-RPC daemon is not configured (Daemon configuration for monero-pools require an additional entry of category \'wallet' pointing to the wallet daemon)", LogCat);
}
ConfigureDaemons();
}
@ -258,7 +261,7 @@ namespace MiningCore.Blockchain.Monero
}
}
public async Task<MoneroShare> SubmitShareAsync(StratumClient worker,
public async Task<Share> SubmitShareAsync(StratumClient worker,
MoneroSubmitShareRequest request, MoneroWorkerJob workerJob, double stratumDifficultyBase)
{
Contract.RequiresNonNull(worker, nameof(worker));
@ -272,28 +275,7 @@ namespace MiningCore.Blockchain.Monero
throw new StratumException(StratumError.MinusOne, "block expired");
// validate & process
var share = job?.ProcessShare(request.Nonce, workerJob.ExtraNonce, request.Hash, worker);
// if block candidate, submit & check if accepted by network
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}]");
share.IsBlockCandidate = await SubmitBlockAsync(share);
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}] submitted by {context.MinerName}");
share.TransactionConfirmationData = share.BlobHash;
}
else
{
// clear fields that no longer apply
share.TransactionConfirmationData = null;
}
}
var (share, blobHex, blobHash) = job.ProcessShare(request.Nonce, workerJob.ExtraNonce, request.Hash, worker);
// enrich share with common data
share.PoolId = poolConfig.Id;
@ -306,6 +288,27 @@ namespace MiningCore.Blockchain.Monero
share.NetworkDifficulty = job.BlockTemplate.Difficulty;
share.Created = clock.Now;
// if block candidate, submit & check if accepted by network
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{blobHash.Substring(0, 6)}]");
share.IsBlockCandidate = await SubmitBlockAsync(share, blobHex, blobHash);
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{blobHash.Substring(0, 6)}] submitted by {context.MinerName}");
share.TransactionConfirmationData = blobHash;
}
else
{
// clear fields that no longer apply
share.TransactionConfirmationData = null;
}
}
return share;
}
@ -322,9 +325,12 @@ namespace MiningCore.Blockchain.Monero
daemon = new DaemonClient(jsonSerializerSettings);
daemon.Configure(daemonEndpoints, MoneroConstants.DaemonRpcLocation);
// also setup wallet daemon
walletDaemon = new DaemonClient(jsonSerializerSettings);
walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation);
if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
{
// also setup wallet daemon
walletDaemon = new DaemonClient(jsonSerializerSettings);
walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation);
}
}
protected override async Task<bool> AreDaemonsHealthyAsync()
@ -340,15 +346,20 @@ namespace MiningCore.Blockchain.Monero
if (!responses.All(x => x.Error == null))
return false;
// test wallet daemons
var responses2 = await walletDaemon.ExecuteCmdAllAsync<object>(MWC.GetAddress);
if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
{
// test wallet daemons
var responses2 = await walletDaemon.ExecuteCmdAllAsync<object>(MWC.GetAddress);
if (responses2.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException))
.Select(x => (DaemonClientException) x.Error.InnerException)
.Any(x => x.Code == HttpStatusCode.Unauthorized))
logger.ThrowLogPoolStartupException($"Wallet-Daemon reports invalid credentials", LogCat);
if (responses2.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException))
.Select(x => (DaemonClientException) x.Error.InnerException)
.Any(x => x.Code == HttpStatusCode.Unauthorized))
logger.ThrowLogPoolStartupException($"Wallet-Daemon reports invalid credentials", LogCat);
return responses2.All(x => x.Error == null);
return responses2.All(x => x.Error == null);
}
return true;
}
protected override async Task<bool> AreDaemonsConnectedAsync()
@ -398,14 +409,18 @@ namespace MiningCore.Blockchain.Monero
protected override async Task PostStartInitAsync()
{
var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo);
var addressResponse = await walletDaemon.ExecuteCmdAnyAsync<GetAddressResponse>(MWC.GetAddress);
if (infoResponse.Error != null)
logger.ThrowLogPoolStartupException($"Init RPC failed: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})", LogCat);
// ensure pool owns wallet
if (clusterConfig.PaymentProcessing?.Enabled == true && addressResponse.Response?.Address != poolConfig.Address)
logger.ThrowLogPoolStartupException($"Wallet-Daemon does not own pool-address '{poolConfig.Address}'", LogCat);
if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
{
var addressResponse = await walletDaemon.ExecuteCmdAnyAsync<GetAddressResponse>(MWC.GetAddress);
// ensure pool owns wallet
if (clusterConfig.PaymentProcessing?.Enabled == true && addressResponse.Response?.Address != poolConfig.Address)
logger.ThrowLogPoolStartupException($"Wallet-Daemon does not own pool-address '{poolConfig.Address}'", LogCat);
}
var info = infoResponse.Response.ToObject<GetInfoResponse>();
@ -430,7 +445,8 @@ namespace MiningCore.Blockchain.Monero
break;
}
ConfigureRewards();
if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
ConfigureRewards();
// update stats
BlockchainStats.RewardType = "POW";

View File

@ -43,7 +43,7 @@ using Newtonsoft.Json;
namespace MiningCore.Blockchain.Monero
{
[CoinMetadata(CoinType.XMR, CoinType.AEON, CoinType.ETN)]
public class MoneroPool : PoolBase<MoneroShare>
public class MoneroPool : PoolBase
{
public MoneroPool(IComponentContext ctx,
JsonSerializerSettings serializerSettings,

View File

@ -1,28 +0,0 @@
/*
Copyright 2017 Coin Foundry (coinfoundry.org)
Authors: Oliver Weichhold (oliver@weichhold.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace MiningCore.Blockchain.Monero
{
public class MoneroShare : ShareBase
{
public string BlobHex { get; set; }
public string BlobHash { get; set; }
}
}

View File

@ -0,0 +1,121 @@
/*
Copyright 2017 Coin Foundry (coinfoundry.org)
Authors: Oliver Weichhold (oliver@weichhold.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using ProtoBuf;
namespace MiningCore.Blockchain
{
[ProtoContract]
public class Share
{
/// <summary>
/// The pool originating this share from
/// </summary>
[ProtoMember(1)]
public string PoolId { get; set; }
/// <summary>
/// Who mined it (wallet address)
/// </summary>
[ProtoMember(2)]
public string Miner { get; set; }
/// <summary>
/// Who mined it
/// </summary>
[ProtoMember(3)]
public string Worker { get; set; }
/// <summary>
/// Extra information for payout processing
/// </summary>
[ProtoMember(4)]
public string PayoutInfo { get; set; }
/// <summary>
/// Mining Software
/// </summary>
[ProtoMember(5)]
public string UserAgent { get; set; }
/// <summary>
/// From where was it submitted
/// </summary>
[ProtoMember(6)]
public string IpAddress { get; set; }
/// <summary>
/// Submission source (pool, external stratum etc)
/// </summary>
[ProtoMember(7)]
public string Source { get; set; }
/// <summary>
/// Stratum difficulty assigned to the miner at the time the share was submitted/accepted (used for payout
/// calculations)
/// </summary>
[ProtoMember(8)]
public double Difficulty { get; set; }
/// <summary>
/// Block this share refers to
/// </summary>
[ProtoMember(9)]
public long BlockHeight { get; set; }
/// <summary>
/// Block reward after deducting pool fee and donations
/// </summary>
[ProtoMember(10)]
public decimal BlockReward { get; set; }
/// <summary>
/// Block hash
/// </summary>
[ProtoMember(11)]
public string BlockHash { get; set; }
/// <summary>
/// If this share presumably resulted in a block
/// </summary>
[ProtoMember(12)]
public bool IsBlockCandidate { get; set; }
/// <summary>
/// Arbitrary data to be interpreted by the payment processor specialized
/// in this coin to verify this block candidate was accepted by the network
/// </summary>
[ProtoMember(13)]
public string TransactionConfirmationData { get; set; }
/// <summary>
/// Network difficulty at the time the share was submitted (used for some payout schemes like PPLNS)
/// </summary>
[ProtoMember(14)]
public double NetworkDifficulty { get; set; }
/// <summary>
/// When the share was found
/// </summary>
[ProtoMember(15)]
public DateTime Created { get; set; }
}
}

View File

@ -1,43 +0,0 @@
/*
Copyright 2017 Coin Foundry (coinfoundry.org)
Authors: Oliver Weichhold (oliver@weichhold.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace MiningCore.Blockchain
{
public class ShareBase : IShare
{
public string PoolId { get; set; }
public string Miner { get; set; }
public string Worker { get; set; }
public string PayoutInfo { get; set; }
public string UserAgent { get; set; }
public string IpAddress { get; set; }
public string Source { get; set; }
public double Difficulty { get; set; }
public double NetworkDifficulty { get; set; }
public long BlockHeight { get; set; }
public decimal BlockReward { get; set; }
public string BlockHash { get; set; }
public bool IsBlockCandidate { get; set; }
public string TransactionConfirmationData { get; set; }
public DateTime Created { get; set; }
}
}

View File

@ -20,11 +20,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using MiningCore.Blockchain.Bitcoin;
using MiningCore.Blockchain.ZCash.DaemonResponses;
using MiningCore.Configuration;
@ -227,7 +225,7 @@ namespace MiningCore.Blockchain.ZCash
#endregion
public override BitcoinShare ProcessShare(StratumClient worker, string extraNonce2, string nTime, string solution)
public override (Share Share, string BlockHex) ProcessShare(StratumClient worker, string extraNonce2, string nTime, string solution)
{
Contract.RequiresNonNull(worker, nameof(worker));
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(extraNonce2), $"{nameof(extraNonce2)} must not be empty");
@ -295,7 +293,7 @@ namespace MiningCore.Blockchain.ZCash
}
}
protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string nonce,
protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string nonce,
uint nTime, string solution)
{
var context = worker.GetContextAs<BitcoinWorkerContext>();
@ -341,22 +339,26 @@ namespace MiningCore.Blockchain.ZCash
throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
}
var result = new BitcoinShare
var result = new Share
{
BlockHeight = BlockTemplate.Height,
IsBlockCandidate = isBlockCandidate,
Difficulty = stratumDifficulty
NetworkDifficulty = Difficulty,
Difficulty = stratumDifficulty,
};
if (isBlockCandidate)
{
var blockBytes = SerializeBlock(headerBytes, coinbaseInitial, solutionBytes);
result.BlockHex = blockBytes.ToHexString();
result.BlockHash = headerHashReversed.ToHexString();
result.IsBlockCandidate = true;
result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC);
result.BlockHash = headerHashReversed.ToHexString();
var blockBytes = SerializeBlock(headerBytes, coinbaseInitial, solutionBytes);
var blockHex = blockBytes.ToHexString();
return (result, blockHex);
}
return result;
return (result, null);
}
protected bool RegisterSubmit(string nonce, string solution)

View File

@ -106,7 +106,7 @@ namespace MiningCore.Blockchain.ZCash
return result;
}
public override async Task<BitcoinShare> SubmitShareAsync(StratumClient worker, object submission,
public override async Task<Share> SubmitShareAsync(StratumClient worker, object submission,
double stratumDifficultyBase)
{
Contract.RequiresNonNull(worker, nameof(worker));
@ -148,14 +148,14 @@ namespace MiningCore.Blockchain.ZCash
var workerName = split.Length > 1 ? split[1] : null;
// validate & process
var share = job.ProcessShare(worker, extraNonce2, nTime, solution);
var (share, blockHex) = job.ProcessShare(worker, extraNonce2, nTime, solution);
// if block candidate, submit & check if accepted by network
if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{share.BlockHash}]");
var acceptResponse = await SubmitBlockAsync(share);
var acceptResponse = await SubmitBlockAsync(share, blockHex);
// is it still a block candidate?
share.IsBlockCandidate = acceptResponse.Accepted;

View File

@ -28,14 +28,14 @@ namespace MiningCore.Mining
{
public struct ClientShare
{
public ClientShare(StratumClient client, IShare share)
public ClientShare(StratumClient client, Share share)
{
Client = client;
Share = share;
}
public StratumClient Client;
public IShare Share;
public Share Share;
}
public interface IMiningPool

View File

@ -52,9 +52,8 @@ using Contract = MiningCore.Contracts.Contract;
namespace MiningCore.Mining
{
public abstract class PoolBase<TShare> : StratumServer,
public abstract class PoolBase : StratumServer,
IMiningPool
where TShare: IShare
{
protected PoolBase(IComponentContext ctx,
JsonSerializerSettings serializerSettings,
@ -139,107 +138,6 @@ namespace MiningCore.Mining
});
}
private void StartExternalStratumPublisherListeners()
{
foreach (var externalStratum in poolConfig.ExternalStratums)
{
var thread = new Thread(arg =>
{
var serializer = new JsonSerializer
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var currentHeight = 0L;
var lastBlockTime = clock.Now;
var config = (ZmqPubSubEndpointConfig) arg;
while (true)
{
try
{
using (var subSocket = new SubscriberSocket())
{
//subSocket.Options.ReceiveHighWatermark = 1000;
subSocket.Connect(config.Url);
subSocket.Subscribe(config.Topic);
logger.Info($"{LogCat}] Monitoring external stratum {config.Url}/{config.Topic}");
while (true)
{
var msg = subSocket.ReceiveMultipartMessage(2);
var topic = msg.Pop().ConvertToString(Encoding.UTF8);
var data = msg.Pop().ConvertToString(Encoding.UTF8);
// validate
if (topic != config.Topic)
{
logger.Warn(() => $"{LogCat}] Received non-matching topic {topic} on ZeroMQ subscriber socket");
continue;
}
if (string.IsNullOrEmpty(data))
{
logger.Warn(() => $"{LogCat}] Received empty data on ZeroMQ subscriber socket");
continue;
}
// deserialize
TShare share;
using (var reader = new StringReader(data))
{
using (var jreader = new JsonTextReader(reader))
{
share = serializer.Deserialize<TShare>(jreader);
}
}
if (share == null)
{
logger.Error(() => $"{LogCat}] Unable to deserialize share received from ZeroMQ subscriber socket");
continue;
}
// update network stats
blockchainStats.BlockHeight = share.BlockHeight;
blockchainStats.NetworkDifficulty = share.NetworkDifficulty;
if (currentHeight != share.BlockHeight)
{
blockchainStats.LastNetworkBlockTime = clock.Now;
currentHeight = share.BlockHeight;
lastBlockTime = clock.Now;
}
else
blockchainStats.LastNetworkBlockTime = lastBlockTime;
// fill in the blacks
share.PoolId = poolConfig.Id;
share.Created = clock.Now;
// re-publish
shareSubject.OnNext(new ClientShare(null, share));
var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}]" : string.Empty;
logger.Info(() => $"[{LogCat}] External {source} share accepted: D={Math.Round(share.Difficulty, 3)}");
}
}
}
catch (Exception ex)
{
logger.Error(ex);
}
}
}) {Name = $"{poolConfig.Id} external stratum listener"};
thread.Start(externalStratum);
}
}
#region VarDiff
protected void UpdateVarDiff(StratumClient client, bool isIdleUpdate = false)
@ -414,7 +312,7 @@ Pool Fee: {(poolConfig.RewardRecipients?.Any() == true ? poolConfi
Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig));
logger = LogUtil.GetPoolScopedLogger(typeof(PoolBase<TShare>), poolConfig);
logger = LogUtil.GetPoolScopedLogger(typeof(PoolBase), poolConfig);
this.poolConfig = poolConfig;
this.clusterConfig = clusterConfig;
}
@ -442,9 +340,6 @@ Pool Fee: {(poolConfig.RewardRecipients?.Any() == true ? poolConfi
StartListeners(poolConfig.Id, ipEndpoints);
}
if(poolConfig.ExternalStratums?.Length > 0)
StartExternalStratumPublisherListeners();
logger.Info(() => $"[{LogCat}] Online");
OutputPoolInfo();
}

View File

@ -28,23 +28,29 @@ using System.Net.Sockets;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Text;
using Autofac.Features.Metadata;
using System.Threading;
using AutoMapper;
using MiningCore.Blockchain;
using MiningCore.Configuration;
using MiningCore.Extensions;
using MiningCore.Mining;
using MiningCore.Notifications;
using MiningCore.Persistence;
using MiningCore.Persistence.Model;
using MiningCore.Persistence.Repositories;
using MiningCore.Time;
using MiningCore.Util;
using NetMQ;
using NetMQ.Sockets;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using NLog;
using Org.BouncyCastle.Utilities.Collections;
using Polly;
using Polly.CircuitBreaker;
using ProtoBuf;
using Contract = MiningCore.Contracts.Contract;
using Share = MiningCore.Blockchain.Share;
namespace MiningCore.Payments
namespace MiningCore.Mining
{
/// <summary>
/// Asynchronously persist shares produced by all pools for processing by coin-specific payment processor(s)
@ -54,6 +60,7 @@ namespace MiningCore.Payments
public ShareRecorder(IConnectionFactory cf, IMapper mapper,
JsonSerializerSettings jsonSerializerSettings,
IShareRepository shareRepo, IBlockRepository blockRepo,
IMasterClock clock,
NotificationService notificationService)
{
Contract.RequiresNonNull(cf, nameof(cf));
@ -61,11 +68,13 @@ namespace MiningCore.Payments
Contract.RequiresNonNull(shareRepo, nameof(shareRepo));
Contract.RequiresNonNull(blockRepo, nameof(blockRepo));
Contract.RequiresNonNull(jsonSerializerSettings, nameof(jsonSerializerSettings));
Contract.RequiresNonNull(clock, nameof(clock));
Contract.RequiresNonNull(notificationService, nameof(notificationService));
this.cf = cf;
this.mapper = mapper;
this.jsonSerializerSettings = jsonSerializerSettings;
this.clock = clock;
this.notificationService = notificationService;
this.shareRepo = shareRepo;
@ -74,16 +83,33 @@ namespace MiningCore.Payments
BuildFaultHandlingPolicy();
}
private static readonly ILogger logger = LogManager.GetCurrentClassLogger();
private readonly IBlockRepository blockRepo;
private readonly IConnectionFactory cf;
private readonly JsonSerializerSettings jsonSerializerSettings;
private readonly IMasterClock clock;
private readonly NotificationService notificationService;
private ClusterConfig clusterConfig;
private readonly IMapper mapper;
private readonly BlockingCollection<IShare> queue = new BlockingCollection<IShare>();
private readonly ConcurrentDictionary<string, PoolContext> pools = new ConcurrentDictionary<string, PoolContext>();
private readonly BlockingCollection<Share> queue = new BlockingCollection<Share>();
class PoolContext
{
public PoolContext(IMiningPool pool, ILogger logger)
{
Pool = pool;
Logger = logger;
}
public IMiningPool Pool;
public ILogger Logger;
public DateTime? LastBlock;
public long BlockHeight;
}
private readonly int QueueSizeWarningThreshold = 1024;
private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(60);
private readonly IShareRepository shareRepo;
private Policy faultPolicy;
private bool hasLoggedPolicyFallbackFailure;
@ -94,22 +120,20 @@ namespace MiningCore.Payments
private const string PolicyContextKeyShares = "share";
private bool notifiedAdminOnPolicyFallback = false;
private static readonly ILogger logger = LogManager.GetCurrentClassLogger();
private void PersistSharesFaulTolerant(IList<IShare> shares)
private void PersistSharesFaulTolerant(IList<Share> shares)
{
var context = new Dictionary<string, object> { { PolicyContextKeyShares, shares } };
faultPolicy.Execute(() => { PersistShares(shares); }, context);
}
private void PersistShares(IList<IShare> shares)
private void PersistShares(IList<Share> shares)
{
cf.RunTx((con, tx) =>
{
foreach(var share in shares)
{
var shareEntity = mapper.Map<Share>(share);
var shareEntity = mapper.Map<Persistence.Model.Share>(share);
shareRepo.Insert(con, tx, shareEntity);
if (share.IsBlockCandidate)
@ -136,7 +160,7 @@ namespace MiningCore.Payments
private void OnExecutePolicyFallback(Context context)
{
var shares = (IList<IShare>) context[PolicyContextKeyShares];
var shares = (IList<Share>) context[PolicyContextKeyShares];
try
{
@ -189,7 +213,7 @@ namespace MiningCore.Payments
{
using(var reader = new StreamReader(stream, new UTF8Encoding(false)))
{
var shares = new List<IShare>();
var shares = new List<Share>();
var lastProgressUpdate = DateTime.UtcNow;
while(!reader.EndOfStream)
@ -207,7 +231,7 @@ namespace MiningCore.Payments
// parse
try
{
var share = JsonConvert.DeserializeObject<ShareBase>(line, jsonSerializerSettings);
var share = JsonConvert.DeserializeObject<Share>(line, jsonSerializerSettings);
shares.Add(share);
}
@ -289,10 +313,167 @@ namespace MiningCore.Payments
}
}
private void StartExternalStratumPublisherListeners()
{
var stratumsByUrl = clusterConfig.Pools.Where(x => x.ExternalStratums?.Any() == true)
.SelectMany(x => x.ExternalStratums)
.Where(x => x.Url != null && x.Topic != null)
.GroupBy(x =>
{
var tmp = x.Url.Trim();
return !tmp.EndsWith("/") ? tmp : tmp.Substring(0, tmp.Length - 1);
}, x=> x.Topic.Trim());
var serializer = new JsonSerializer
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
foreach (var item in stratumsByUrl)
{
var thread = new Thread(arg =>
{
var urlAndTopic = (IGrouping<string, string>) arg;
var url = urlAndTopic.Key;
var topics = new HashSet<string>(urlAndTopic.Distinct());
var receivedOnce = false;
while (true)
{
try
{
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect(url);
// subscribe to all topics
foreach (var topic in topics)
subSocket.Subscribe(topic);
logger.Info($"Monitoring external stratum {url}/[{string.Join(", ", topics)}]");
while (true)
{
// receive
var msg = (NetMQMessage)null;
if (!subSocket.TryReceiveMultipartMessage(relayReceiveTimeout, ref msg, 3))
{
if (receivedOnce)
{
logger.Warn(() => $"Timeout receiving message from {url}. Reconnecting ...");
break;
}
// retry
continue;
}
// extract frames
var topic = msg.Pop().ConvertToString(Encoding.UTF8);
var flags = msg.Pop().ConvertToInt32();
var data = msg.Pop().ToByteArray();
receivedOnce = true;
// validate
if (!topics.Contains(topic))
{
logger.Warn(() => $"Received non-matching topic {topic} on ZeroMQ subscriber socket");
continue;
}
if (data?.Length == 0)
{
logger.Warn(() => $"Received empty data from {url}/{topic}");
continue;
}
// deserialize
var wireFormat = (ShareRelay.WireFormat)(flags & ShareRelay.WireFormatMask);
Share share = null;
switch (wireFormat)
{
case ShareRelay.WireFormat.Json:
using (var stream = new MemoryStream(data))
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
using (var jreader = new JsonTextReader(reader))
{
share = serializer.Deserialize<Share>(jreader);
}
}
}
break;
case ShareRelay.WireFormat.ProtocolBuffers:
using (var stream = new MemoryStream(data))
{
share = Serializer.Deserialize<Share>(stream);
}
break;
default:
logger.Error(() => $"Unsupported wire format {wireFormat} of share received from {url}/{topic} ");
break;
}
if (share == null)
{
logger.Error(() => $"Unable to deserialize share received from {url}/{topic}");
continue;
}
// store
share.PoolId = topic;
share.Created = clock.Now;
queue.Add(share);
// misc
if (pools.TryGetValue(topic, out var poolContext))
{
var pool = poolContext.Pool;
poolContext.Logger.Info(() => $"External {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}share accepted: D={Math.Round(share.Difficulty, 3)}");
// update pool stats
if (pool.NetworkStats != null)
{
pool.NetworkStats.BlockHeight = share.BlockHeight;
pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty;
if (poolContext.BlockHeight != share.BlockHeight)
{
pool.NetworkStats.LastNetworkBlockTime = clock.Now;
poolContext.BlockHeight = share.BlockHeight;
poolContext.LastBlock = clock.Now;
}
else
pool.NetworkStats.LastNetworkBlockTime = poolContext.LastBlock;
}
}
}
}
}
catch (Exception ex)
{
logger.Error(ex);
}
}
});
thread.Start(item);
}
}
#region API-Surface
public void AttachPool(IMiningPool pool)
{
pools[pool.Config.Id] = new PoolContext(pool, LogUtil.GetPoolScopedLogger(typeof(ShareRecorder), pool.Config));
pool.Shares.Subscribe(x => { queue.Add(x.Share); });
}
@ -302,6 +483,7 @@ namespace MiningCore.Payments
ConfigureRecovery();
InitializeQueue();
StartExternalStratumPublisherListeners();
logger.Info(() => "Online");
}

View File

@ -1,19 +1,17 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Text;
using MiningCore.Blockchain;
using MiningCore.Configuration;
using MiningCore.Mining;
using NetMQ;
using NetMQ.Sockets;
using Newtonsoft.Json;
using NLog;
using ProtoBuf;
namespace MiningCore.Payments
namespace MiningCore.Mining
{
public class ShareRelay
{
@ -23,7 +21,7 @@ namespace MiningCore.Payments
}
private ClusterConfig clusterConfig;
private readonly BlockingCollection<IShare> queue = new BlockingCollection<IShare>();
private readonly BlockingCollection<Share> queue = new BlockingCollection<Share>();
private IDisposable queueSub;
private readonly int QueueSizeWarningThreshold = 1024;
private bool hasWarnedAboutBacklogSize;
@ -32,6 +30,15 @@ namespace MiningCore.Payments
private static readonly ILogger logger = LogManager.GetCurrentClassLogger();
[Flags]
public enum WireFormat
{
Json = 1,
ProtocolBuffers = 2
}
public const int WireFormatMask = 0xF;
#region API-Surface
public void AttachPool(IMiningPool pool)
@ -87,10 +94,16 @@ namespace MiningCore.Payments
try
{
var json = JsonConvert.SerializeObject(share, serializerSettings);
var flags = (int) WireFormat.ProtocolBuffers;
var msg = new NetMQMessage(2);
msg.Push(json);
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, share);
msg.Push(stream.ToArray());
}
msg.Push(flags);
msg.Push(share.PoolId);
pubSocket.SendMultipartMessage(msg);
}

View File

@ -46,21 +46,22 @@
<PackageReference Include="Autofac" Version="4.6.2" />
<PackageReference Include="AutoMapper" Version="6.2.2" />
<PackageReference Include="Dapper" Version="1.50.4" />
<PackageReference Include="FluentValidation" Version="7.3.4" />
<PackageReference Include="FluentValidation" Version="7.5.1" />
<PackageReference Include="JetBrains.Annotations" Version="11.1.0" />
<PackageReference Include="MailKit" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.0" />
<PackageReference Include="MailKit" Version="2.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="NBitcoin" Version="4.0.0.51" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.1" />
<PackageReference Include="NBitcoin" Version="4.0.0.65" />
<PackageReference Include="NetMQ" Version="4.0.0.1" />
<PackageReference Include="NetUV.Core" Version="0.1.140" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
<PackageReference Include="NLog" Version="5.0.0-beta09" />
<PackageReference Include="Npgsql" Version="3.2.6" />
<PackageReference Include="Polly" Version="5.6.1" />
<PackageReference Include="Npgsql" Version="3.2.7" />
<PackageReference Include="Polly" Version="5.8.0" />
<PackageReference Include="protobuf-net" Version="2.3.7" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
</ItemGroup>

View File

@ -39,7 +39,7 @@ namespace MiningCore.Native
private static extern UInt64 decode_integrated_address(byte* input, int inputSize);
[DllImport("libcryptonote", EntryPoint = "cn_slow_hash_export", CallingConvention = CallingConvention.Cdecl)]
private static extern int cn_slow_hash(byte* input, byte* output, uint inputLength);
private static extern int cn_slow_hash(byte* input, byte* output, uint inputLength, int variant);
[DllImport("libcryptonote", EntryPoint = "cn_slow_hash_lite_export", CallingConvention = CallingConvention.Cdecl)]
private static extern int cn_slow_hash_lite(byte* input, byte* output, uint inputLength);
@ -124,7 +124,7 @@ namespace MiningCore.Native
}
}
public static PooledArraySegment<byte> CryptonightHashSlow(byte[] data)
public static PooledArraySegment<byte> CryptonightHashSlow(byte[] data, int variant)
{
Contract.RequiresNonNull(data, nameof(data));
@ -134,7 +134,7 @@ namespace MiningCore.Native
{
fixed(byte* output = result.Array)
{
cn_slow_hash(input, output, (uint) data.Length);
cn_slow_hash(input, output, (uint) data.Length, variant);
}
}

View File

@ -46,7 +46,7 @@ namespace MiningCore.Persistence.Postgres.Repositories
private readonly IMapper mapper;
private readonly IMasterClock clock;
private static readonly ILogger logger = LogManager.GetCurrentClassLogger();
private static readonly TimeSpan MinerStatsMaxAge = TimeSpan.FromMinutes(15);
private static readonly TimeSpan MinerStatsMaxAge = TimeSpan.FromMinutes(20);
public void InsertPoolStats(IDbConnection con, IDbTransaction tx, PoolStats stats)
{
@ -146,7 +146,7 @@ namespace MiningCore.Persistence.Postgres.Repositories
var lastUpdate = con.QuerySingleOrDefault<DateTime?>(query, new { poolId, miner }, tx);
// ignore stale minerstats
if (lastUpdate.HasValue && (clock.Now - lastUpdate) > MinerStatsMaxAge)
if (lastUpdate.HasValue && (clock.Now - DateTime.SpecifyKind(lastUpdate.Value, DateTimeKind.Utc) > MinerStatsMaxAge))
lastUpdate = null;
if (lastUpdate.HasValue)

View File

@ -647,7 +647,7 @@ namespace MiningCore
private static void TouchNativeLibs()
{
Console.WriteLine(LibCryptonote.CryptonightHashSlow(Encoding.UTF8.GetBytes("test")).ToHexString());
Console.WriteLine(LibCryptonote.CryptonightHashSlow(Encoding.UTF8.GetBytes("test"), 0).ToHexString());
Console.WriteLine(LibCryptonote.CryptonightHashFast(Encoding.UTF8.GetBytes("test")).ToHexString());
Console.WriteLine(new Blake().Digest(Encoding.UTF8.GetBytes("test"), 0).ToHexString());
}

View File

@ -1,8 +1,8 @@
{
"profiles": {
"MiningCore": {
"commandName": "Project",
"commandLineArgs": "-c config.json"
}
}
{
"profiles": {
"MiningCore": {
"commandName": "Project",
"commandLineArgs": "-c ..\\..\\..\\config.json"
}
}
}

View File

@ -7,7 +7,8 @@ LDLIBS = -lboost_system -lboost_date_time
TARGET = libcryptonote.so
OBJECTS = contrib/epee/src/hex.o \
common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha8.o \
common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha.o \
contrib/epee/src/memwipe.o \
crypto/crypto-ops-data.o crypto/crypto-ops.o crypto/crypto.o crypto/groestl.o crypto/hash-extra-blake.o \
crypto/hash-extra-groestl.o crypto/hash-extra-jh.o crypto/hash-extra-skein.o crypto/hash.o crypto/jh.o \
crypto/keccak.o crypto/oaes_lib.o crypto/random.o crypto/skein.o crypto/slow-hash.o crypto/tree-hash.o \

View File

@ -7,7 +7,8 @@ LDLIBS = -lboost_system-mt -lboost_date_time-mt
TARGET = libcryptonote.dll
OBJECTS = contrib/epee/src/hex.o \
common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha8.o \
common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha.o \
contrib/epee/src/memwipe.o \
crypto/crypto-ops-data.o crypto/crypto-ops.o crypto/crypto.o crypto/groestl.o crypto/hash-extra-blake.o \
crypto/hash-extra-groestl.o crypto/hash-extra-jh.o crypto/hash-extra-skein.o crypto/hash.o crypto/jh.o \
crypto/keccak.o crypto/oaes_lib.o crypto/random.o crypto/skein.o crypto/slow-hash.o crypto/tree-hash.o \

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -36,7 +36,7 @@
#include "crypto/hash.h"
#include "int-util.h"
// OW: unused #include "util.h"
#include "util.h"
#include "varint.h"
namespace tools
@ -111,13 +111,13 @@ namespace tools
uint64_t res = 0;
switch (9 - size)
{
case 1: res |= *data++;
case 2: res <<= 8; res |= *data++;
case 3: res <<= 8; res |= *data++;
case 4: res <<= 8; res |= *data++;
case 5: res <<= 8; res |= *data++;
case 6: res <<= 8; res |= *data++;
case 7: res <<= 8; res |= *data++;
case 1: res |= *data++; /* FALLTHRU */
case 2: res <<= 8; res |= *data++; /* FALLTHRU */
case 3: res <<= 8; res |= *data++; /* FALLTHRU */
case 4: res <<= 8; res |= *data++; /* FALLTHRU */
case 5: res <<= 8; res |= *data++; /* FALLTHRU */
case 6: res <<= 8; res |= *data++; /* FALLTHRU */
case 7: res <<= 8; res |= *data++; /* FALLTHRU */
case 8: res <<= 8; res |= *data; break;
default: assert(false);
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -35,7 +35,7 @@
#include <stdint.h>
#include <string.h>
#if !defined(_MSC_VER)
#ifndef _MSC_VER
#include <sys/param.h>
#endif
@ -43,6 +43,10 @@
#include <byteswap.h>
#endif
#if defined(__sun) && defined(__SVR4)
#include <endian.h>
#endif
#if defined(_MSC_VER)
#include <stdlib.h>
@ -205,13 +209,14 @@ static inline void memcpy_swap64(void *dst, const void *src, size_t n) {
}
}
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
#if !defined(_MSC_VER)
static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled");
#else
#define LITTLE_ENDIAN 1234
#define BYTE_ORDER LITTLE_ENDIAN
#ifdef _MSC_VER
# define LITTLE_ENDIAN 1234
# define BIG_ENDIAN 4321
# define BYTE_ORDER LITTLE_ENDIAN
#endif
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled");
#endif
#if BYTE_ORDER == LITTLE_ENDIAN

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//

View File

@ -0,0 +1,215 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <system_error>
#include <csignal>
#include <cstdio>
#include <functional>
#include <memory>
#include <string>
#ifdef _WIN32
#include "windows.h"
#include "misc_log_ex.h"
#endif
#include "crypto/hash.h"
/*! \brief Various Tools
*
*
*
*/
namespace tools
{
//! Functional class for closing C file handles.
struct close_file
{
void operator()(std::FILE* handle) const noexcept
{
if (handle)
{
std::fclose(handle);
}
}
};
//! A file restricted to process owner AND process. Deletes file on destruction.
class private_file {
std::unique_ptr<std::FILE, close_file> m_handle;
std::string m_filename;
private_file(std::FILE* handle, std::string&& filename) noexcept;
public:
//! `handle() == nullptr && filename.empty()`.
private_file() noexcept;
/*! \return File only readable by owner and only used by this process
OR `private_file{}` on error. */
static private_file create(std::string filename);
private_file(private_file&&) = default;
private_file& operator=(private_file&&) = default;
//! Deletes `filename()` and closes `handle()`.
~private_file() noexcept;
std::FILE* handle() const noexcept { return m_handle.get(); }
const std::string& filename() const noexcept { return m_filename; }
};
/*! \brief Returns the default data directory.
*
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
*
* Windows >= Vista: C:\\Users\\Username\\AppData\\Roaming\\CRYPTONOTE_NAME
*
* Mac: ~/Library/Application Support/CRYPTONOTE_NAME
*
* Unix: ~/.CRYPTONOTE_NAME
*/
std::string get_default_data_dir();
#ifdef WIN32
/**
* @brief
*
* @param nfolder
* @param iscreate
*
* @return
*/
std::string get_special_folder_path(int nfolder, bool iscreate);
#endif
/*! \brief Returns the OS version string
*
* \details This is a wrapper around the primitives
* get_windows_version_display_string() and
* get_nix_version_display_string()
*/
std::string get_os_version_string();
/*! \brief creates directories for a path
*
* wrapper around boost::filesyste::create_directories.
* (ensure-directory-exists): greenspun's tenth rule in action!
*/
bool create_directories_if_necessary(const std::string& path);
/*! \brief std::rename wrapper for nix and something strange for windows.
*/
std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name);
bool sanitize_locale();
bool on_startup();
/*! \brief Defines a signal handler for win32 and *nix
*/
class signal_handler
{
public:
/*! \brief installs a signal handler */
template<typename T>
static bool install(T t)
{
#if defined(WIN32)
bool r = TRUE == ::SetConsoleCtrlHandler(&win_handler, TRUE);
if (r)
{
m_handler = t;
}
return r;
#else
static struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = posix_handler;
sa.sa_flags = 0;
/* Only blocks SIGINT, SIGTERM and SIGPIPE */
sigaction(SIGINT, &sa, NULL);
signal(SIGTERM, posix_handler);
signal(SIGPIPE, SIG_IGN);
m_handler = t;
return true;
#endif
}
private:
#if defined(WIN32)
/*! \brief Handler for win */
static BOOL WINAPI win_handler(DWORD type)
{
if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type)
{
handle_signal(type);
}
else
{
MGINFO_RED("Got control signal " << type << ". Exiting without saving...");
return FALSE;
}
return TRUE;
}
#else
/*! \brief handler for NIX */
static void posix_handler(int type)
{
handle_signal(type);
}
#endif
/*! \brief calles m_handler */
static void handle_signal(int type)
{
static boost::mutex m_mutex;
boost::unique_lock<boost::mutex> lock(m_mutex);
m_handler(type);
}
/*! \brief where the installed handler is stored */
static std::function<void(int)> m_handler;
};
void set_strict_default_file_permissions(bool strict);
void set_max_concurrency(unsigned n);
unsigned get_max_concurrency();
bool is_local_address(const std::string &address);
int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate
bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash);
bool sha256sum(const std::string &filename, crypto::hash &hash);
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -45,7 +45,7 @@
* is as follows: Strip the msb of each byte, then from left to right,
* read in what remains, placing it in reverse, into the buffer. Thus,
* the following bit stream: 0xff02 would return 0x027f. 0xff turns
* into 0x7f, is placed on the beggining of the buffer, then 0x02 is
* into 0x7f, is placed on the beginning of the buffer, then 0x02 is
* unchanged, since its msb is not set, and placed at the end of the
* buffer.
*/
@ -108,7 +108,7 @@ namespace tools {
return EVARINT_REPRESENT;
}
write |= static_cast<T>(byte & 0x7f) << shift; /* Does the actualy placing into write, stripping the first bit */
write |= static_cast<T>(byte & 0x7f) << shift; /* Does the actually placing into write, stripping the first bit */
/* If there is no next */
if ((byte & 0x80) == 0) {

View File

@ -27,6 +27,7 @@
#pragma once
#include "misc_log_ex.h"
#include "string_tools.h"
#include <atomic>
#include <condition_variable>
#include <functional>
@ -37,6 +38,8 @@
#include <stdio.h>
#endif
#include <boost/thread.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#ifdef HAVE_READLINE
#include "readline_buffer.h"
@ -456,29 +459,35 @@ eof:
class command_handler {
public:
typedef boost::function<bool (const std::vector<std::string> &)> callback;
typedef std::map<std::string, std::pair<callback, std::string> > lookup;
typedef std::map<std::string, std::pair<callback, std::pair<std::string, std::string>>> lookup;
std::string get_usage()
{
std::stringstream ss;
size_t max_command_len = 0;
for(auto& x:m_command_handlers)
if(x.first.size() > max_command_len)
max_command_len = x.first.size();
for(auto& x:m_command_handlers)
{
ss.width(max_command_len + 3);
ss << std::left << x.first << x.second.second << ENDL;
ss << x.second.second.first << ENDL;
}
return ss.str();
}
void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "")
std::pair<std::string, std::string> get_documentation(const std::vector<std::string>& cmd)
{
if(cmd.empty())
return std::make_pair("", "");
auto it = m_command_handlers.find(cmd.front());
if(it == m_command_handlers.end())
return std::make_pair("", "");
return it->second.second;
}
void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "", const std::string& description = "")
{
lookup::mapped_type & vt = m_command_handlers[cmd];
vt.first = hndlr;
vt.second = usage;
vt.second.first = description.empty() ? cmd : usage;
vt.second.second = description.empty() ? usage : description;
#ifdef HAVE_READLINE
rdln::readline_buffer::add_completion(cmd);
#endif

View File

@ -35,6 +35,8 @@ namespace epee
public:
copyable_atomic()
{};
copyable_atomic(uint32_t value)
{ store(value); }
copyable_atomic(const copyable_atomic& a):std::atomic<uint32_t>(a.load())
{}
copyable_atomic& operator= (const copyable_atomic& a)

View File

@ -29,7 +29,33 @@
#define _FILE_IO_UTILS_H_
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#ifdef WIN32
#include <windows.h>
#endif
// On Windows there is a problem with non-ASCII characters in path and file names
// as far as support by the standard components used is concerned:
// The various file stream classes, e.g. std::ifstream and std::ofstream, are
// part of the GNU C++ Library / libstdc++. On the most basic level they use the
// fopen() call as defined / made accessible to programs compiled within MSYS2
// by the stdio.h header file maintained by the MinGW project.
// The critical point: The implementation of fopen() is part of MSVCRT, the
// Microsoft Visual C/C++ Runtime Library, and this method does NOT offer any
// Unicode support.
// Monero code that would want to continue to use the normal file stream classes
// but WITH Unicode support could therefore not solve this problem on its own,
// but 2 different projects from 2 different maintaining groups would need changes
// in this particular direction - something probably difficult to achieve and
// with a long time to wait until all new versions / releases arrive.
// Implemented solution approach: Circumvent the problem by stopping to use std
// file stream classes on Windows and directly use Unicode-capable WIN32 API
// calls. Most of the code doing so is concentrated in this header file here.
namespace epee
{
@ -45,7 +71,22 @@ namespace file_io_utils
inline
bool save_string_to_file(const std::string& path_to_file, const std::string& str)
{
#ifdef WIN32
WCHAR wide_path[1000];
int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000);
if (chars == 0)
return false;
HANDLE file_handle = CreateFileW(wide_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file_handle == INVALID_HANDLE_VALUE)
return false;
DWORD bytes_written;
DWORD bytes_to_write = (DWORD)str.size();
BOOL result = WriteFile(file_handle, str.data(), bytes_to_write, &bytes_written, NULL);
CloseHandle(file_handle);
if (bytes_written != bytes_to_write)
result = FALSE;
return result;
#else
try
{
std::ofstream fstream;
@ -60,10 +101,11 @@ namespace file_io_utils
{
return false;
}
#endif
}
inline
bool get_file_time(const std::string& path_to_file, OUT time_t& ft)
bool get_file_time(const std::string& path_to_file, time_t& ft)
{
boost::system::error_code ec;
ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec);
@ -88,6 +130,27 @@ namespace file_io_utils
inline
bool load_file_to_string(const std::string& path_to_file, std::string& target_str)
{
#ifdef WIN32
WCHAR wide_path[1000];
int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000);
if (chars == 0)
return false;
HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file_handle == INVALID_HANDLE_VALUE)
return false;
DWORD file_size = GetFileSize(file_handle, NULL);
if ((file_size == INVALID_FILE_SIZE) || (file_size > 1000000000)) {
CloseHandle(file_handle);
return false;
}
target_str.resize(file_size);
DWORD bytes_read;
BOOL result = ReadFile(file_handle, &target_str[0], file_size, &bytes_read, NULL);
CloseHandle(file_handle);
if (bytes_read != file_size)
result = FALSE;
return result;
#else
try
{
std::ifstream fstream;
@ -112,11 +175,13 @@ namespace file_io_utils
{
return false;
}
#endif
}
inline
bool append_string_to_file(const std::string& path_to_file, const std::string& str)
{
// No special Windows implementation because so far not used in Monero code
try
{
std::ofstream fstream;
@ -132,6 +197,43 @@ namespace file_io_utils
return false;
}
}
inline
bool get_file_size(const std::string& path_to_file, uint64_t &size)
{
#ifdef WIN32
WCHAR wide_path[1000];
int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000);
if (chars == 0)
return false;
HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file_handle == INVALID_HANDLE_VALUE)
return false;
LARGE_INTEGER file_size;
BOOL result = GetFileSizeEx(file_handle, &file_size);
CloseHandle(file_handle);
if (result) {
size = file_size.QuadPart;
}
return size;
#else
try
{
std::ifstream fstream;
fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate);
size = fstream.tellg();
fstream.close();
return true;
}
catch(...)
{
return false;
}
#endif
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017, The Monero Project
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//

View File

@ -37,6 +37,7 @@
#include <boost/uuid/random_generator.hpp>
#include "misc_os_dependent.h"
#include "syncobj.h"
namespace epee
{

View File

@ -0,0 +1,80 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#ifdef __cplusplus
#include <array>
extern "C" {
#endif
void *memwipe(void *src, size_t n);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
namespace tools {
/// Scrubs data in the contained type upon destruction.
///
/// Primarily useful for making sure that private keys don't stick around in
/// memory after the objects that held them have gone out of scope.
template <class T>
struct scrubbed : public T {
using type = T;
~scrubbed() {
scrub();
}
/// Destroy the contents of the contained type.
void scrub() {
static_assert(std::is_pod<T>::value,
"T cannot be auto-scrubbed. T must be POD.");
static_assert(std::is_trivially_destructible<T>::value,
"T cannot be auto-scrubbed. T must be trivially destructable.");
memwipe(this, sizeof(T));
}
};
template<typename T>
T& unwrap(scrubbed<T>& src) { return src; }
template<typename T>
const T& unwrap(scrubbed<T> const& src) { return src; }
template <class T, size_t N>
using scrubbed_arr = scrubbed<std::array<T, N>>;
} // namespace tools
#endif // __cplusplus

View File

@ -23,6 +23,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifdef _WIN32
#include <Winsock2.h>
#endif
#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
@ -42,6 +46,9 @@
#include <mach/mach.h>
#endif
#include <iostream>
#include <boost/lexical_cast.hpp>
#pragma once
namespace epee
{
@ -68,13 +75,13 @@ namespace misc_utils
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
return (mts.tv_sec * 1000000000) + (mts.tv_nsec);
return ((uint64_t)mts.tv_sec * 1000000000) + (mts.tv_nsec);
#else
struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
return 0;
}
return (ts.tv_sec * 1000000000) + (ts.tv_nsec);
return ((uint64_t)ts.tv_sec * 1000000000) + (ts.tv_nsec);
#endif
}

View File

@ -305,7 +305,7 @@ namespace net_utils
m_connections.back().powner = this;
m_connections.back().m_self_it = --m_connections.end();
m_connections.back().m_context.m_remote_address = remote_address;
m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back());
m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back()); // ugh, seems very risky
return true;
}

View File

@ -54,8 +54,8 @@
#include <boost/thread/thread.hpp>
#include "net_utils_base.h"
#include "syncobj.h"
#include "../../../../src/p2p/connection_basic.hpp"
#include "../../../../src/p2p/network_throttle-detail.hpp"
#include "connection_basic.hpp"
#include "network_throttle-detail.hpp"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net"
@ -207,12 +207,18 @@ namespace net_utils
bool connect(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0");
template<class t_callback>
bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_callback cb, const std::string& bind_ip = "0.0.0.0");
bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0");
typename t_protocol_handler::config_type& get_config_object(){return m_config;}
int get_binded_port(){return m_port;}
long get_connections_count() const
{
auto connections_count = (m_sock_count > 0) ? (m_sock_count - 1) : 0; // Socket count minus listening socket
return connections_count;
}
boost::asio::io_service& get_io_service(){return io_service_;}
struct idle_callback_conext_base
@ -281,8 +287,6 @@ namespace net_utils
bool is_thread_worker();
bool cleanup_connections();
/// The io_service used to perform asynchronous operations.
std::unique_ptr<boost::asio::io_service> m_io_service_local_instance;
boost::asio::io_service& io_service_;
@ -309,7 +313,7 @@ namespace net_utils
connection_ptr new_connection_;
boost::mutex connections_mutex;
std::deque<std::pair<boost::system_time, connection_ptr>> connections_;
std::set<connection_ptr> connections_;
}; // class <>boosted_tcp_server

View File

@ -54,8 +54,6 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net"
#define CONNECTION_CLEANUP_TIME 30 // seconds
PRAGMA_WARNING_PUSH
namespace epee
{
@ -82,7 +80,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
m_throttle_speed_in("speed_in", "throttle_speed_in"),
m_throttle_speed_out("speed_out", "throttle_speed_out")
{
MINFO("test, connection constructor set m_connection_type="<<m_connection_type);
MDEBUG("test, connection constructor set m_connection_type="<<m_connection_type);
}
PRAGMA_WARNING_DISABLE_VS(4355)
//---------------------------------------------------------------------------------
@ -139,14 +137,14 @@ PRAGMA_WARNING_DISABLE_VS(4355)
CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value());
context = boost::value_initialized<t_connection_context>();
long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong());
const unsigned long ip_{boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong())};
// create a random uuid
boost::uuids::uuid random_uuid;
// that stuff turns out to be included, even though it's from src... Taking advantage
random_uuid = crypto::rand<boost::uuids::uuid>();
context.set_details(random_uuid, new epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income);
context.set_details(random_uuid, epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income);
_dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) <<
" to " << local_ep.address().to_string() << ':' << local_ep.port() <<
", total sockets objects " << m_ref_sock_count);
@ -266,7 +264,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
address = endpoint.address().to_string();
port = boost::lexical_cast<std::string>(endpoint.port());
}
MINFO(" connection type " << to_string( m_connection_type ) << " "
MDEBUG(" connection type " << to_string( m_connection_type ) << " "
<< socket_.local_endpoint().address().to_string() << ":" << socket_.local_endpoint().port()
<< " <--> " << address << ":" << port);
}
@ -288,7 +286,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
{
CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::network_throttle_manager::m_lock_get_global_throttle_in );
epee::net_utils::network_throttle_manager::network_throttle_manager::get_global_throttle_in().handle_trafic_exact(bytes_transferred * 1024);
epee::net_utils::network_throttle_manager::network_throttle_manager::get_global_throttle_in().handle_trafic_exact(bytes_transferred);
}
double delay=0; // will be calculated - how much we should sleep to obey speed limit etc
@ -299,7 +297,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
{
{ //_scope_dbg1("CRITICAL_REGION_LOCAL");
CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in );
delay = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick( bytes_transferred ); // decission from global throttle
delay = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick( bytes_transferred );
}
delay *= 0.5;
@ -484,9 +482,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
//some data should be wrote to stream
//request complete
if (speed_limit_is_enabled()) {
sleep_before_packet(cb, 1, 1);
}
// No sleeping here; sleeping is done once and for all in "handle_write"
m_send_que_lock.lock(); // *** critical ***
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_send_que_lock.unlock();});
@ -609,6 +605,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
}
logger_handle_net_write(cb);
// The single sleeping that is needed for correctly handling "out" speed throttling
if (speed_limit_is_enabled()) {
sleep_before_packet(cb, 1, 1);
}
@ -738,7 +735,17 @@ PRAGMA_WARNING_DISABLE_VS(4355)
boost::asio::placeholders::error));
return true;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::init_server", false);
}
catch (const std::exception &e)
{
MFATAL("Error starting server: " << e.what());
return false;
}
catch (...)
{
MFATAL("Error starting server");
return false;
}
}
//-----------------------------------------------------------------------------
PUSH_WARNINGS
@ -808,7 +815,6 @@ POP_WARNINGS
m_threads_count = threads_count;
m_main_thread_id = boost::this_thread::get_id();
MLOG_SET_THREAD_NAME("[SRV_MAIN]");
add_idle_handler(boost::bind(&boosted_tcp_server::cleanup_connections, this), 5000);
while(!m_stop_signal_sent)
{
@ -898,7 +904,7 @@ POP_WARNINGS
connections_mutex.lock();
for (auto &c: connections_)
{
c.second->cancel();
c->cancel();
}
connections_.clear();
connections_mutex.unlock();
@ -907,19 +913,6 @@ POP_WARNINGS
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::cleanup_connections()
{
connections_mutex.lock();
boost::system_time cutoff = boost::get_system_time() - boost::posix_time::seconds(CONNECTION_CLEANUP_TIME);
while (!connections_.empty() && connections_.front().first < cutoff)
{
connections_.pop_front();
}
connections_mutex.unlock();
return true;
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::is_stop_signal_sent()
{
return m_stop_signal_sent;
@ -942,6 +935,9 @@ POP_WARNINGS
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
boost::asio::placeholders::error));
boost::asio::socket_base::keep_alive opt(true);
conn->socket().set_option(opt);
conn->start(true, 1 < m_threads_count);
conn->save_dbg_log();
}else
@ -958,9 +954,10 @@ POP_WARNINGS
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) );
connections_mutex.lock();
connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l));
connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
connections_mutex.unlock();
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
//////////////////////////////////////////////////////////////////////////
@ -1038,6 +1035,10 @@ POP_WARNINGS
_dbg3("Connected success to " << adr << ':' << port);
// start adds the connection to the config object's list, so we don't need to have it locally anymore
connections_mutex.lock();
connections_.erase(new_connection_l);
connections_mutex.unlock();
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r)
{
@ -1057,14 +1058,15 @@ POP_WARNINGS
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler> template<class t_callback>
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_callback cb, const std::string& bind_ip)
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip)
{
TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) );
connections_mutex.lock();
connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l));
connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
connections_mutex.unlock();
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
//////////////////////////////////////////////////////////////////////////
@ -1113,6 +1115,11 @@ POP_WARNINGS
{
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
" from " << lep.address().to_string() << ':' << lep.port());
// start adds the connection to the config object's list, so we don't need to have it locally anymore
connections_mutex.lock();
connections_.erase(new_connection_l);
connections_mutex.unlock();
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r)
{

View File

@ -0,0 +1,141 @@
/// @file
/// @author rfree (current maintainer in monero.cc project)
/// @brief base for connection, contains e.g. the ratelimit hooks
// ! This file might contain variable names same as in template class connection<>
// ! from files contrib/epee/include/net/abstract_tcp_server2.*
// ! I am not a lawyer; afaik APIs, var names etc are not copyrightable ;)
// ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part)
// ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as:
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* rfree: place for hanlers for the non-template base, can be used by connection<> template class in abstract_tcp_server2 file */
#ifndef INCLUDED_p2p_connection_basic_hpp
#define INCLUDED_p2p_connection_basic_hpp
#include <boost/asio.hpp>
#include <string>
#include <vector>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <atomic>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/interprocess/detail/atomic.hpp>
#include <boost/thread/thread.hpp>
#include <memory>
#include "net/net_utils_base.h"
#include "syncobj.h"
namespace epee
{
namespace net_utils
{
/************************************************************************/
/* */
/************************************************************************/
/// Represents a single connection from a client.
class connection_basic_pimpl; // PIMPL for this class
enum t_connection_type { // type of the connection (of this server), e.g. so that we will know how to limit it
e_connection_type_NET = 0, // default (not used?)
e_connection_type_RPC = 1, // the rpc commands (probably not rate limited, not chunked, etc)
e_connection_type_P2P = 2 // to other p2p node (probably limited)
};
std::string to_string(t_connection_type type);
class connection_basic { // not-templated base class for rapid developmet of some code parts
public:
std::unique_ptr< connection_basic_pimpl > mI; // my Implementation
// moved here from orginal connecton<> - common member variables that do not depend on template in connection<>
volatile uint32_t m_want_close_connection;
std::atomic<bool> m_was_shutdown;
critical_section m_send_que_lock;
std::list<std::string> m_send_que;
volatile bool m_is_multithreaded;
double m_start_time;
/// Strand to ensure the connection's handlers are not called concurrently.
boost::asio::io_service::strand strand_;
/// Socket for the connection.
boost::asio::ip::tcp::socket socket_;
std::atomic<long> &m_ref_sock_count; // reference to external counter of existing sockets that we will ++/--
public:
// first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator
connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number);
virtual ~connection_basic() noexcept(false);
// various handlers to be called from connection class:
void do_send_handler_write(const void * ptr , size_t cb);
void do_send_handler_write_from_queue(const boost::system::error_code& e, size_t cb , int q_len); // from handle_write, sending next part
void logger_handle_net_write(size_t size); // network data written
void logger_handle_net_read(size_t size); // network data read
void set_start_time();
// config for rate limit
static void set_rate_up_limit(uint64_t limit);
static void set_rate_down_limit(uint64_t limit);
static uint64_t get_rate_up_limit();
static uint64_t get_rate_down_limit();
// config misc
static void set_tos_flag(int tos); // ToS / QoS flag
static int get_tos_flag();
// handlers and sleep
void sleep_before_packet(size_t packet_size, int phase, int q_len); // execute a sleep ; phase is not really used now(?)
static void save_limit_to_file(int limit); ///< for dr-monero
static double get_sleep_time(size_t cb);
static void set_save_graph(bool save_graph);
};
} // nameserver
} // nameserver
#endif

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -33,7 +33,7 @@
#include <functional>
#include <string>
#include <utility>
#include "wipeable_string.h"
#include "http_base.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@ -48,12 +48,12 @@ namespace net_utils
struct login
{
login() : username(), password() {}
login(std::string username_, std::string password_)
login(std::string username_, wipeable_string password_)
: username(std::move(username_)), password(std::move(password_))
{}
std::string username;
std::string password;
wipeable_string password;
};
//! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added.
@ -71,8 +71,8 @@ namespace net_utils
std::uint32_t counter;
};
http_server_auth() : user() {}
http_server_auth(login credentials);
http_server_auth() : user(), rng() {}
http_server_auth(login credentials, std::function<void(size_t, uint8_t*)> r);
//! \return Auth response, or `boost::none` iff `request` had valid auth.
boost::optional<http_response_info> get_response(const http_request_info& request)
@ -81,10 +81,13 @@ namespace net_utils
return do_get_response(request);
return boost::none;
}
private:
boost::optional<http_response_info> do_get_response(const http_request_info& request);
boost::optional<session> user;
std::function<void(size_t, uint8_t*)> rng;
};
//! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added.

View File

@ -46,6 +46,7 @@ namespace net_utils
{
enum http_method{
http_method_options,
http_method_get,
http_method_post,
http_method_put,
@ -115,6 +116,7 @@ namespace net_utils
std::string m_host; //"Host:"
std::string m_cookie; //"Cookie:"
std::string m_user_agent; //"User-Agent:"
std::string m_origin; //"Origin:"
fields_list m_etc_fields;
void clear()
@ -128,6 +130,7 @@ namespace net_utils
m_host.clear();
m_cookie.clear();
m_user_agent.clear();
m_origin.clear();
m_etc_fields.clear();
}
};
@ -155,7 +158,8 @@ namespace net_utils
http_request_info():m_http_method(http_method_unknown),
m_http_ver_hi(0),
m_http_ver_lo(0),
m_have_to_block(false)
m_have_to_block(false),
m_full_request_buf_size(0)
{}
http_method m_http_method;

View File

@ -27,6 +27,7 @@
#pragma once
#include <ctype.h>
#include <boost/shared_ptr.hpp>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
@ -50,6 +51,7 @@
#include "http_auth.h"
#include "to_nonconst_iterator.h"
#include "net_parse_helpers.h"
#include "syncobj.h"
//#include "shlwapi.h"
@ -66,8 +68,6 @@ namespace epee
namespace net_utils
{
using namespace std;
/*struct url
{
public:
@ -237,7 +237,8 @@ using namespace std;
namespace http
{
class http_simple_client: public i_target_handler
template<typename net_client_type>
class http_simple_client_template: public i_target_handler
{
private:
enum reciev_machine_state
@ -260,7 +261,7 @@ using namespace std;
};
blocked_mode_client m_net_client;
net_client_type m_net_client;
std::string m_host_buff;
std::string m_port;
http_client_auth m_auth;
@ -274,9 +275,10 @@ using namespace std;
chunked_state m_chunked_state;
std::string m_chunked_cache;
critical_section m_lock;
bool m_ssl;
public:
explicit http_simple_client()
explicit http_simple_client_template()
: i_target_handler()
, m_net_client()
, m_host_buff()
@ -291,33 +293,35 @@ using namespace std;
, m_chunked_state()
, m_chunked_cache()
, m_lock()
, m_ssl(false)
{}
const std::string &get_host() const { return m_host_buff; };
const std::string &get_port() const { return m_port; };
bool set_server(const std::string& address, boost::optional<login> user)
bool set_server(const std::string& address, boost::optional<login> user, bool ssl = false)
{
http::url_content parsed{};
const bool r = parse_url(address, parsed);
CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address);
set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user));
set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), ssl);
return true;
}
void set_server(std::string host, std::string port, boost::optional<login> user)
void set_server(std::string host, std::string port, boost::optional<login> user, bool ssl = false)
{
CRITICAL_REGION_LOCAL(m_lock);
disconnect();
m_host_buff = std::move(host);
m_port = std::move(port);
m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{};
m_ssl = ssl;
}
bool connect(std::chrono::milliseconds timeout)
{
CRITICAL_REGION_LOCAL(m_lock);
return m_net_client.connect(m_host_buff, m_port, timeout);
return m_net_client.connect(m_host_buff, m_port, timeout, m_ssl);
}
//---------------------------------------------------------------------------
bool disconnect()
@ -392,7 +396,6 @@ using namespace std;
res = m_net_client.send(body, timeout);
CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
m_response_info.clear();
m_state = reciev_machine_state_header;
if (!handle_reciev(timeout))
@ -427,6 +430,15 @@ using namespace std;
CRITICAL_REGION_LOCAL(m_lock);
return invoke(uri, "POST", body, timeout, ppresponse_info, additional_params);
}
//---------------------------------------------------------------------------
bool test(const std::string &s, std::chrono::milliseconds timeout) // TEST FUNC ONLY
{
CRITICAL_REGION_LOCAL(m_lock);
m_net_client.set_test_data(s);
m_state = reciev_machine_state_header;
return handle_reciev(timeout);
}
//---------------------------------------------------------------------------
private:
//---------------------------------------------------------------------------
inline bool handle_reciev(std::chrono::milliseconds timeout)
@ -741,85 +753,107 @@ using namespace std;
return true;
}
//---------------------------------------------------------------------------
inline
bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process)
{
MTRACE("http_stream_filter::parse_cached_header(*)");
STATIC_REGEXP_EXPR_1(rexp_mach_field,
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)"
// 12 3 4 5 6 7 8 9 10
"|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
//11 1213 14
boost::regex::icase | boost::regex::normal);
boost::smatch result;
std::string::const_iterator it_current_bound = m_cache_to_process.begin();
std::string::const_iterator it_end_bound = m_cache_to_process.end();
//lookup all fields and fill well-known fields
while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
{
const size_t field_val = 13;
//const size_t field_etc_name = 11;
int i = 2; //start position = 2
if(result[i++].matched)//"Connection"
body_info.m_connection = result[field_val];
else if(result[i++].matched)//"Referrer"
body_info.m_referer = result[field_val];
else if(result[i++].matched)//"Content-Length"
body_info.m_content_length = result[field_val];
else if(result[i++].matched)//"Content-Type"
body_info.m_content_type = result[field_val];
else if(result[i++].matched)//"Transfer-Encoding"
body_info.m_transfer_encoding = result[field_val];
else if(result[i++].matched)//"Content-Encoding"
body_info.m_content_encoding = result[field_val];
else if(result[i++].matched)//"Host"
{ body_info.m_host = result[field_val];
string_tools::trim(body_info.m_host);
}
else if(result[i++].matched)//"Cookie"
body_info.m_cookie = result[field_val];
else if(result[i++].matched)//"User-Agent"
body_info.m_user_agent = result[field_val];
else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
body_info.m_etc_fields.emplace_back(result[11], result[field_val]);
else
{CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);}
it_current_bound = result[(int)result.size()-1]. first;
}
return true;
}
inline
bool analize_first_response_line()
inline bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process)
{
MTRACE("http_stream_filter::parse_cached_header(*)");
//First line response, look like this: "HTTP/1.1 200 OK"
STATIC_REGEXP_EXPR_1(rexp_match_first_response_line, "^HTTP/(\\d+).(\\d+) ((\\d)\\d{2})( [^\n]*)?\r?\n", boost::regex::icase | boost::regex::normal);
// 1 2 34 5
//size_t match_len = 0;
boost::smatch result;
if(boost::regex_search( m_header_cache, result, rexp_match_first_response_line, boost::match_default) && result[0].matched)
const char *ptr = m_cache_to_process.c_str();
while (ptr[0] != '\r' || ptr[1] != '\n')
{
CHECK_AND_ASSERT_MES(result[1].matched&&result[2].matched, false, "http_stream_filter::handle_invoke_reply_line() assert failed...");
m_response_info.m_http_ver_hi = boost::lexical_cast<int>(result[1]);
m_response_info.m_http_ver_lo = boost::lexical_cast<int>(result[2]);
m_response_info.m_response_code = boost::lexical_cast<int>(result[3]);
m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second));
return true;
}else
{
LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache);
return false;
// optional \n
if (*ptr == '\n')
++ptr;
// an identifier composed of letters or -
const char *key_pos = ptr;
while (isalnum(*ptr) || *ptr == '_' || *ptr == '-')
++ptr;
const char *key_end = ptr;
// optional space (not in RFC, but in previous code)
if (*ptr == ' ')
++ptr;
CHECK_AND_ASSERT_MES(*ptr == ':', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process);
++ptr;
// optional whitespace, but not newlines - line folding is obsolete, let's ignore it
while (isblank(*ptr))
++ptr;
const char *value_pos = ptr;
while (*ptr != '\r' && *ptr != '\n')
++ptr;
const char *value_end = ptr;
// optional trailing whitespace
while (value_end > value_pos && isblank(*(value_end-1)))
--value_end;
if (*ptr == '\r')
++ptr;
CHECK_AND_ASSERT_MES(*ptr == '\n', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process);
++ptr;
const std::string key = std::string(key_pos, key_end - key_pos);
const std::string value = std::string(value_pos, value_end - value_pos);
if (!key.empty())
{
if (!string_tools::compare_no_case(key, "Connection"))
body_info.m_connection = value;
else if(!string_tools::compare_no_case(key, "Referrer"))
body_info.m_referer = value;
else if(!string_tools::compare_no_case(key, "Content-Length"))
body_info.m_content_length = value;
else if(!string_tools::compare_no_case(key, "Content-Type"))
body_info.m_content_type = value;
else if(!string_tools::compare_no_case(key, "Transfer-Encoding"))
body_info.m_transfer_encoding = value;
else if(!string_tools::compare_no_case(key, "Content-Encoding"))
body_info.m_content_encoding = value;
else if(!string_tools::compare_no_case(key, "Host"))
body_info.m_host = value;
else if(!string_tools::compare_no_case(key, "Cookie"))
body_info.m_cookie = value;
else if(!string_tools::compare_no_case(key, "User-Agent"))
body_info.m_user_agent = value;
else if(!string_tools::compare_no_case(key, "Origin"))
body_info.m_origin = value;
else
body_info.m_etc_fields.emplace_back(key, value);
}
}
return true;
}
//---------------------------------------------------------------------------
inline bool analize_first_response_line()
{
//First line response, look like this: "HTTP/1.1 200 OK"
const char *ptr = m_header_cache.c_str();
CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line: " + m_header_cache);
ptr += 5;
CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache);
unsigned long ul;
char *end;
ul = strtoul(ptr, &end, 10);
CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line: " + m_header_cache);
m_response_info.m_http_ver_hi = ul;
ptr = end + 1;
CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr);
ul = strtoul(ptr, &end, 10);
CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr);
m_response_info.m_http_ver_lo = ul;
ptr = end + 1;
while (isblank(*ptr))
++ptr;
CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache);
ul = strtoul(ptr, &end, 10);
CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line: " + m_header_cache);
m_response_info.m_response_code = ul;
ptr = end;
// ignore the optional text, till the end
while (*ptr != '\r' && *ptr != '\n')
++ptr;
if (*ptr == '\r')
++ptr;
CHECK_AND_ASSERT_MES(*ptr == '\n', false, "Invalid first response line: " << m_header_cache);
++ptr;
m_header_cache.erase(0, ptr - m_header_cache.c_str());
return true;
}
inline
bool set_reply_content_encoder()
@ -954,6 +988,7 @@ using namespace std;
return true;
}
};
typedef http_simple_client_template<blocked_mode_client> http_simple_client;
}
}
}

View File

@ -38,8 +38,8 @@ namespace epee
virtual ~i_sub_handler(){}
virtual bool update_in( std::string& piece_of_transfer)=0;
virtual void stop(std::string& OUT collect_remains)=0;
virtual bool update_and_stop(std::string& OUT collect_remains, bool& is_changed)
virtual void stop(std::string& collect_remains)=0;
virtual bool update_and_stop(std::string& collect_remains, bool& is_changed)
{
is_changed = true;
bool res = this->update_in(collect_remains);
@ -66,7 +66,7 @@ namespace epee
{
return m_powner_filter->handle_target_data(piece_of_transfer);
}
virtual void stop(std::string& OUT collect_remains)
virtual void stop(std::string& collect_remains)
{
}

View File

@ -54,6 +54,7 @@ namespace net_utils
struct http_server_config
{
std::string m_folder;
std::vector<std::string> m_access_control_origins;
boost::optional<login> m_user;
critical_section m_lock;
};
@ -159,6 +160,7 @@ namespace net_utils
struct custum_handler_config: public http_server_config
{
i_http_server_handler<t_connection_context>* m_phandler;
std::function<void(size_t, uint8_t*)> rng;
};
/************************************************************************/
@ -175,7 +177,7 @@ namespace net_utils
: simple_http_connection_handler<t_connection_context>(psnd_hndlr, config),
m_config(config),
m_conn_context(conn_context),
m_auth(m_config.m_user ? http_server_auth{*m_config.m_user} : http_server_auth{})
m_auth(m_config.m_user ? http_server_auth{*m_config.m_user, config.rng} : http_server_auth{})
{}
inline bool handle_request(const http_request_info& query_info, http_response_info& response)
{
@ -193,6 +195,7 @@ namespace net_utils
response.m_response_code = 200;
response.m_response_comment = "OK";
response.m_body.clear();
return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context);
}

View File

@ -316,7 +316,10 @@ namespace net_utils
CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed...");
http_ver_major = boost::lexical_cast<int>(result[11]);
http_ver_minor = boost::lexical_cast<int>(result[12]);
if(result[4].matched)
if(result[3].matched)
method = http::http_method_options;
else if(result[4].matched)
method = http::http_method_get;
else if(result[5].matched)
method = http::http_method_head;
@ -342,7 +345,12 @@ namespace net_utils
{
analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi);
m_query_info.m_URI = result[10];
parse_uri(m_query_info.m_URI, m_query_info.m_uri_content);
if (!parse_uri(m_query_info.m_URI, m_query_info.m_uri_content))
{
m_state = http_state_error;
MERROR("Failed to parse URI: m_query_info.m_URI");
return false;
}
m_query_info.m_http_method_str = result[2];
m_query_info.m_full_request_str = result[0];
@ -472,8 +480,8 @@ namespace net_utils
bool simple_http_connection_handler<t_connection_context>::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos)
{
STATIC_REGEXP_EXPR_1(rexp_mach_field,
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)"
// 12 3 4 5 6 7 8 9 10
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)"
// 12 3 4 5 6 7 8 9 10 11
"|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
//11 1213 14
boost::regex::icase | boost::regex::normal);
@ -487,8 +495,8 @@ namespace net_utils
//lookup all fields and fill well-known fields
while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
{
const size_t field_val = 13;
const size_t field_etc_name = 11;
const size_t field_val = 14;
const size_t field_etc_name = 12;
int i = 2; //start position = 2
if(result[i++].matched)//"Connection"
@ -509,6 +517,8 @@ namespace net_utils
body_info.m_cookie = result[field_val];
else if(result[i++].matched)//"User-Agent"
body_info.m_user_agent = result[field_val];
else if(result[i++].matched)//"Origin"
body_info.m_origin = result[field_val];
else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
else
@ -537,17 +547,27 @@ namespace net_utils
template<class t_connection_context>
bool simple_http_connection_handler<t_connection_context>::handle_request_and_send_response(const http::http_request_info& query_info)
{
http_response_info response;
bool res = handle_request(query_info, response);
http_response_info response{};
//CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" );
bool res = true;
if (query_info.m_http_method != http::http_method_options)
{
res = handle_request(query_info, response);
}
else
{
response.m_response_code = 200;
response.m_response_comment = "OK";
}
std::string response_data = get_response_header(response);
//LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body);
LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data);
m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size());
if(response.m_body.size() && (query_info.m_http_method != http::http_method_head))
if ((response.m_body.size() && (query_info.m_http_method != http::http_method_head)) || (query_info.m_http_method == http::http_method_options))
m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size());
return res;
}
@ -579,7 +599,6 @@ namespace net_utils
response.m_response_comment = "OK";
response.m_mime_tipe = get_file_mime_tipe(uri_to_path);
return true;
}
//-----------------------------------------------------------------------------------
@ -591,8 +610,12 @@ namespace net_utils
"Server: Epee-based\r\n"
"Content-Length: ";
buf += boost::lexical_cast<std::string>(response.m_body.size()) + "\r\n";
buf += "Content-Type: ";
buf += response.m_mime_tipe + "\r\n";
if(!response.m_mime_tipe.empty())
{
buf += "Content-Type: ";
buf += response.m_mime_tipe + "\r\n";
}
buf += "Last-Modified: ";
time_t tm;
@ -612,6 +635,22 @@ namespace net_utils
m_want_close = true;
}
}
// Cross-origin resource sharing
if(m_query_info.m_header_info.m_origin.size())
{
if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin))
{
buf += "Access-Control-Allow-Origin: ";
buf += m_query_info.m_header_info.m_origin;
buf += "\r\n";
buf += "Access-Control-Expose-Headers: www-authenticate\r\n";
if (m_query_info.m_http_method == http::http_method_options)
buf += "Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With\r\n";
buf += "Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS\r\n";
}
}
//add additional fields, if it is
for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++)
buf += it->first + ":" + it->second + "\r\n";

View File

@ -55,16 +55,22 @@ namespace epee
: m_net_server(external_io_service)
{}
bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
bool init(std::function<void(size_t, uint8_t*)> rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
std::vector<std::string> access_control_origins = std::vector<std::string>(),
boost::optional<net_utils::http::login> user = boost::none)
{
//set self as callback handler
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
m_net_server.get_config_object().rng = std::move(rng);
//here set folder for hosting reqests
m_net_server.get_config_object().m_folder = "";
//set access control allow origins if configured
std::sort(access_control_origins.begin(), access_control_origins.end());
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
m_net_server.get_config_object().m_user = std::move(user);
MGINFO("Binding on " << bind_ip << ":" << bind_port);
@ -112,6 +118,11 @@ namespace epee
return m_net_server.get_binded_port();
}
long get_connections_count() const
{
return m_net_server.get_connections_count();
}
protected:
net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
};

View File

@ -87,6 +87,7 @@ namespace levin
virtual void on_connection_new(t_connection_context& context){};
virtual void on_connection_close(t_connection_context& context){};
virtual ~levin_commands_handler(){}
};
#define LEVIN_OK 0

View File

@ -52,6 +52,7 @@ namespace levin
class levin_client_async
{
levin_commands_handler* m_pcommands_handler;
void (*commands_handler_destroy)(levin_commands_handler*);
volatile uint32_t m_is_stop;
volatile uint32_t m_threads_count;
::critical_section m_send_lock;
@ -85,9 +86,9 @@ namespace levin
::critical_section m_connection_lock;
net_utils::blocked_mode_client m_transport;
public:
levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
levin_client_async():m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
{}
levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
{}
~levin_client_async()
{
@ -97,11 +98,16 @@ namespace levin
while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count))
::Sleep(100);
set_handler(NULL);
}
void set_handler(levin_commands_handler* phandler)
void set_handler(levin_commands_handler* phandler, void (*destroy)(levin_commands_handler*) = NULL)
{
if (commands_handler_destroy && m_pcommands_handler)
(*commands_handler_destroy)(m_pcommands_handler);
m_pcommands_handler = phandler;
m_pcommands_handler_destroy = destroy;
}
bool connect(uint32_t ip, uint32_t port, uint32_t timeout)

View File

@ -43,6 +43,8 @@ namespace levin
struct protocl_handler_config
{
levin_commands_handler<t_connection_context>* m_pcommands_handler;
void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*);
~protocl_handler_config() { if (m_pcommands_handler && m_pcommands_handler_destroy) (*m_pcommands_handler_destroy)(m_pcommands_handler); }
};
template<class t_connection_context = net_utils::connection_context_base>

View File

@ -35,6 +35,8 @@
#include "levin_base.h"
#include "misc_language.h"
#include "syncobj.h"
#include "misc_os_dependent.h"
#include <random>
#include <chrono>
@ -72,29 +74,36 @@ class async_protocol_handler_config
friend class async_protocol_handler<t_connection_context>;
levin_commands_handler<t_connection_context>* m_pcommands_handler;
void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*);
void delete_connections (size_t count, bool incoming);
public:
typedef t_connection_context connection_context;
levin_commands_handler<t_connection_context>* m_pcommands_handler;
uint64_t m_max_packet_size;
uint64_t m_invoke_timeout;
int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id);
template<class callback_t>
int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED);
int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED);
int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id);
bool close(boost::uuids::uuid connection_id);
bool update_connection_context(const t_connection_context& contxt);
bool request_callback(boost::uuids::uuid connection_id);
template<class callback_t>
bool foreach_connection(callback_t cb);
bool foreach_connection(const callback_t &cb);
template<class callback_t>
bool for_connection(const boost::uuids::uuid &connection_id, callback_t cb);
bool for_connection(const boost::uuids::uuid &connection_id, const callback_t &cb);
size_t get_connections_count();
void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL);
async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
{}
~async_protocol_handler_config() { set_handler(NULL, NULL); }
void del_out_connections(size_t count);
void del_in_connections(size_t count);
};
@ -239,7 +248,7 @@ public:
std::list<boost::shared_ptr<invoke_response_handler_base> > m_invoke_response_handlers;
template<class callback_t>
bool add_invoke_response_handler(callback_t cb, uint64_t timeout, async_protocol_handler& con, int command)
bool add_invoke_response_handler(const callback_t &cb, uint64_t timeout, async_protocol_handler& con, int command)
{
CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock);
boost::shared_ptr<invoke_response_handler_base> handler(boost::make_shared<anvoke_handler<callback_t>>(cb, timeout, con, command));
@ -373,12 +382,16 @@ public:
if(m_cache_in_buffer.size() < m_current_head.m_cb)
{
is_continue = false;
if(cb >= MIN_BYTES_WANTED && !m_invoke_response_handlers.empty())
if(cb >= MIN_BYTES_WANTED)
{
//async call scenario
boost::shared_ptr<invoke_response_handler_base> response_handler = m_invoke_response_handlers.front();
response_handler->reset_timer();
MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb);
CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock);
if (!m_invoke_response_handlers.empty())
{
//async call scenario
boost::shared_ptr<invoke_response_handler_base> response_handler = m_invoke_response_handlers.front();
response_handler->reset_timer();
MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb);
}
}
break;
}
@ -519,7 +532,7 @@ public:
}
template<class callback_t>
bool async_invoke(int command, const std::string& in_buff, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
bool async_invoke(int command, const std::string& in_buff, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
{
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
boost::bind(&async_protocol_handler::finish_outer_call, this));
@ -721,32 +734,50 @@ void async_protocol_handler_config<t_connection_context>::del_connection(async_p
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
void async_protocol_handler_config<t_connection_context>::delete_connections(size_t count, bool incoming)
{
std::vector <boost::uuids::uuid> connections;
CRITICAL_REGION_BEGIN(m_connects_lock);
for (auto& c: m_connects)
{
if (c.second->m_connection_context.m_is_income == incoming)
connections.push_back(c.first);
}
// close random connections from the provided set
// TODO or better just keep removing random elements (performance)
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
shuffle(connections.begin(), connections.end(), std::default_random_engine(seed));
while (count > 0 && connections.size() > 0)
{
try
{
auto i = connections.end() - 1;
async_protocol_handler<t_connection_context> *conn = m_connects.at(*i);
del_connection(conn);
close(*i);
connections.erase(i);
}
catch (const std::out_of_range &e)
{
MWARNING("Connection not found in m_connects, continuing");
}
--count;
}
CRITICAL_REGION_END();
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
void async_protocol_handler_config<t_connection_context>::del_out_connections(size_t count)
{
std::vector <boost::uuids::uuid> out_connections;
CRITICAL_REGION_BEGIN(m_connects_lock);
for (auto& c: m_connects)
{
if (!c.second->m_connection_context.m_is_income)
out_connections.push_back(c.first);
}
if (out_connections.size() == 0)
return;
// close random out connections
// TODO or better just keep removing random elements (performance)
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
shuffle(out_connections.begin(), out_connections.end(), std::default_random_engine(seed));
while (count > 0 && out_connections.size() > 0)
{
close(*out_connections.begin());
del_connection(m_connects.at(*out_connections.begin()));
out_connections.erase(out_connections.begin());
--count;
}
CRITICAL_REGION_END();
delete_connections(count, false);
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
void async_protocol_handler_config<t_connection_context>::del_in_connections(size_t count)
{
delete_connections(count, true);
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
@ -786,7 +817,7 @@ int async_protocol_handler_config<t_connection_context>::invoke(int command, con
}
//------------------------------------------------------------------------------------------
template<class t_connection_context> template<class callback_t>
int async_protocol_handler_config<t_connection_context>::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout)
int async_protocol_handler_config<t_connection_context>::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout)
{
async_protocol_handler<t_connection_context>* aph;
int r = find_and_lock_connection(connection_id, aph);
@ -794,7 +825,7 @@ int async_protocol_handler_config<t_connection_context>::invoke_async(int comman
}
//------------------------------------------------------------------------------------------
template<class t_connection_context> template<class callback_t>
bool async_protocol_handler_config<t_connection_context>::foreach_connection(callback_t cb)
bool async_protocol_handler_config<t_connection_context>::foreach_connection(const callback_t &cb)
{
CRITICAL_REGION_LOCAL(m_connects_lock);
for(auto& c: m_connects)
@ -807,7 +838,7 @@ bool async_protocol_handler_config<t_connection_context>::foreach_connection(cal
}
//------------------------------------------------------------------------------------------
template<class t_connection_context> template<class callback_t>
bool async_protocol_handler_config<t_connection_context>::for_connection(const boost::uuids::uuid &connection_id, callback_t cb)
bool async_protocol_handler_config<t_connection_context>::for_connection(const boost::uuids::uuid &connection_id, const callback_t &cb)
{
CRITICAL_REGION_LOCAL(m_connects_lock);
async_protocol_handler<t_connection_context>* aph = find_connection(connection_id);
@ -826,6 +857,15 @@ size_t async_protocol_handler_config<t_connection_context>::get_connections_coun
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
void async_protocol_handler_config<t_connection_context>::set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*))
{
if (m_pcommands_handler && m_pcommands_handler_destroy)
(*m_pcommands_handler_destroy)(m_pcommands_handler);
m_pcommands_handler = handler;
m_pcommands_handler_destroy = destroy;
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id)
{
async_protocol_handler<t_connection_context>* aph;

View File

@ -31,21 +31,16 @@
//#include <Winsock2.h>
//#include <Ws2tcpip.h>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <boost/version.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/preprocessor/selection/min.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/interprocess/detail/atomic.hpp>
#include "net/net_utils_base.h"
#include "misc_language.h"
//#include "profile_tools.h"
#include "../string_tools.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net"
@ -85,11 +80,13 @@ namespace net_utils
public:
inline
blocked_mode_client():m_socket(m_io_service),
m_initialized(false),
blocked_mode_client():m_initialized(false),
m_connected(false),
m_deadline(m_io_service),
m_shutdowned(0)
m_shutdowned(0),
m_ssl(false),
m_ctx(boost::asio::ssl::context::sslv23),
m_ssl_socket(m_io_service,m_ctx)
{
@ -113,18 +110,25 @@ namespace net_utils
}
inline
bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0")
bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0")
{
return connect(addr, std::to_string(port), timeout, bind_ip);
return connect(addr, std::to_string(port), timeout, ssl, bind_ip);
}
inline
bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0")
bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0")
{
m_connected = false;
m_ssl = ssl;
try
{
m_socket.close();
m_ssl_socket.next_layer().close();
// Set SSL options
// disable sslv2
m_ctx.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2);
m_ctx.set_default_verify_paths();
// Get a list of endpoints corresponding to the server name.
@ -147,11 +151,11 @@ namespace net_utils
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
m_socket.open(remote_endpoint.protocol());
m_ssl_socket.next_layer().open(remote_endpoint.protocol());
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
{
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0);
m_socket.bind(local_endpoint);
m_ssl_socket.next_layer().bind(local_endpoint);
}
@ -160,17 +164,24 @@ namespace net_utils
boost::system::error_code ec = boost::asio::error::would_block;
//m_socket.connect(remote_endpoint);
m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1);
m_ssl_socket.next_layer().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1);
while (ec == boost::asio::error::would_block)
{
m_io_service.run_one();
}
if (!ec && m_socket.is_open())
if (!ec && m_ssl_socket.next_layer().is_open())
{
m_connected = true;
m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
// SSL Options
if(m_ssl) {
// Disable verification of host certificate
m_ssl_socket.set_verify_mode(boost::asio::ssl::verify_peer);
// Handshake
m_ssl_socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true));
m_ssl_socket.handshake(boost::asio::ssl::stream_base::client);
}
return true;
}else
{
@ -193,7 +204,6 @@ namespace net_utils
return true;
}
inline
bool disconnect()
{
@ -202,8 +212,9 @@ namespace net_utils
if(m_connected)
{
m_connected = false;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
if(m_ssl)
shutdown_ssl();
m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
}
}
@ -240,7 +251,7 @@ namespace net_utils
// object is used as a callback and will update the ec variable when the
// operation completes. The blocking_udp_client.cpp example shows how you
// can use boost::bind rather than boost::lambda.
boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1);
async_write(buff.c_str(), buff.size(), ec);
// Block until the asynchronous operation has completed.
while (ec == boost::asio::error::would_block)
@ -302,9 +313,7 @@ namespace net_utils
*/
boost::system::error_code ec;
size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
size_t writen = write(data, sz, ec);
if (!writen || ec)
{
@ -334,10 +343,7 @@ namespace net_utils
bool is_connected()
{
return m_connected && m_socket.is_open();
//TRY_ENTRY()
//return m_socket.is_open();
//CATCH_ENTRY_L0("is_connected", false)
return m_connected && m_ssl_socket.next_layer().is_open();
}
inline
@ -369,8 +375,8 @@ namespace net_utils
handler_obj hndlr(ec, bytes_transfered);
char local_buff[10000] = {0};
//m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr);
boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr);
async_read(local_buff, sizeof(local_buff), boost::asio::transfer_at_least(1), hndlr);
// Block until the asynchronous operation has completed.
while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
@ -451,10 +457,8 @@ namespace net_utils
handler_obj hndlr(ec, bytes_transfered);
//char local_buff[10000] = {0};
boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr);
async_read((char*)buff.data(), buff.size(), boost::asio::transfer_at_least(buff.size()), hndlr);
// Block until the asynchronous operation has completed.
while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
{
@ -500,10 +504,18 @@ namespace net_utils
bool shutdown()
{
m_deadline.cancel();
boost::system::error_code ignored_ec;
m_socket.cancel(ignored_ec);
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
m_socket.close(ignored_ec);
boost::system::error_code ec;
if(m_ssl)
shutdown_ssl();
m_ssl_socket.next_layer().cancel(ec);
if(ec)
MDEBUG("Problems at cancel: " << ec.message());
m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
if(ec)
MDEBUG("Problems at shutdown: " << ec.message());
m_ssl_socket.next_layer().close(ec);
if(ec)
MDEBUG("Problems at close: " << ec.message());
boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1);
m_connected = false;
return true;
@ -520,7 +532,7 @@ namespace net_utils
boost::asio::ip::tcp::socket& get_socket()
{
return m_socket;
return m_ssl_socket.next_layer();
}
private:
@ -537,7 +549,7 @@ namespace net_utils
// connect(), read_line() or write_line() functions to return.
LOG_PRINT_L3("Timed out socket");
m_connected = false;
m_socket.close();
m_ssl_socket.next_layer().close();
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.
@ -547,12 +559,61 @@ namespace net_utils
// Put the actor back to sleep.
m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this));
}
void shutdown_ssl() {
// ssl socket shutdown blocks if server doesn't respond. We close after 2 secs
boost::system::error_code ec = boost::asio::error::would_block;
m_deadline.expires_from_now(std::chrono::milliseconds(2000));
m_ssl_socket.async_shutdown(boost::lambda::var(ec) = boost::lambda::_1);
while (ec == boost::asio::error::would_block)
{
m_io_service.run_one();
}
// Ignore "short read" error
if (ec.category() == boost::asio::error::get_ssl_category() &&
ec.value() !=
#if BOOST_VERSION >= 106200
boost::asio::ssl::error::stream_truncated
#else // older Boost supports only OpenSSL 1.0, so 1.0-only macros are appropriate
ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)
#endif
)
MDEBUG("Problems at ssl shutdown: " << ec.message());
}
protected:
bool write(const void* data, size_t sz, boost::system::error_code& ec)
{
bool success;
if(m_ssl)
success = boost::asio::write(m_ssl_socket, boost::asio::buffer(data, sz), ec);
else
success = boost::asio::write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), ec);
return success;
}
void async_write(const void* data, size_t sz, boost::system::error_code& ec)
{
if(m_ssl)
boost::asio::async_write(m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
else
boost::asio::async_write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
}
void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr)
{
if(!m_ssl)
boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr);
else
boost::asio::async_read(m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr);
}
protected:
boost::asio::io_service m_io_service;
boost::asio::ip::tcp::socket m_socket;
boost::asio::ssl::context m_ctx;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> m_ssl_socket;
bool m_ssl;
bool m_initialized;
bool m_connected;
boost::asio::steady_timer m_deadline;
@ -618,8 +679,8 @@ namespace net_utils
boost::system::error_code ec;
size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
size_t writen = write(data, sz, ec);
if (!writen || ec)
{
LOG_PRINT_L3("Problems at write: " << ec.message());
@ -660,7 +721,7 @@ namespace net_utils
// asynchronous operations are cancelled. This allows the blocked
// connect(), read_line() or write_line() functions to return.
LOG_PRINT_L3("Timed out socket");
m_socket.close();
m_ssl_socket.next_layer().close();
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.

View File

@ -103,7 +103,7 @@ namespace net_utils
STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal);
boost::smatch result;
if(!boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched)
if(!(boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched))
{
LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << uri);
content.m_path = uri;
@ -139,7 +139,7 @@ namespace net_utils
// 12 34 5 6 7
content.port = 0;
boost::smatch result;
if(!boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched)
if(!(boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched))
{
LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << rexp_match_uri);
//content.m_path = uri;

View File

@ -29,12 +29,11 @@
#ifndef _NET_UTILS_BASE_H_
#define _NET_UTILS_BASE_H_
#include <typeinfo>
#include <boost/asio/io_service.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/asio/io_service.hpp>
#include <typeinfo>
#include <type_traits>
#include "serialization/keyvalue_serialization.h"
#include "net/local_ip.h"
#include "string_tools.h"
#include "misc_log_ex.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@ -44,108 +43,176 @@
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
#endif
namespace epee
{
namespace net_utils
{
struct network_address_base
class ipv4_network_address
{
public:
bool operator==(const network_address_base &other) const { return m_full_id == other.m_full_id; }
bool operator!=(const network_address_base &other) const { return !operator==(other); }
bool operator<(const network_address_base &other) const { return m_full_id < other.m_full_id; }
bool is_same_host(const network_address_base &other) const { return m_host_id == other.m_host_id; }
virtual std::string str() const = 0;
virtual std::string host_str() const = 0;
virtual bool is_loopback() const = 0;
virtual bool is_local() const = 0;
virtual uint8_t get_type_id() const = 0;
protected:
// A very simple non cryptographic hash function by Fowler, Noll, Vo
uint64_t fnv1a(const uint8_t *data, size_t len) const {
uint64_t h = 0xcbf29ce484222325;
while (len--)
h = (h ^ *data++) * 0x100000001b3;
return h;
}
uint64_t m_host_id;
uint64_t m_full_id;
};
struct ipv4_network_address: public network_address_base
{
void init_ids()
{
m_host_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip));
m_full_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip) + sizeof(m_port));
}
public:
ipv4_network_address(uint32_t ip, uint16_t port): network_address_base(), m_ip(ip), m_port(port) { init_ids(); }
uint32_t ip() const { return m_ip; }
uint16_t port() const { return m_port; }
virtual std::string str() const { return epee::string_tools::get_ip_string_from_int32(m_ip) + ":" + std::to_string(m_port); }
virtual std::string host_str() const { return epee::string_tools::get_ip_string_from_int32(m_ip); }
virtual bool is_loopback() const { return epee::net_utils::is_ip_loopback(m_ip); }
virtual bool is_local() const { return epee::net_utils::is_ip_local(m_ip); }
virtual uint8_t get_type_id() const { return ID; }
public: // serialization
static const uint8_t ID = 1;
#pragma pack(push)
#pragma pack(1)
uint32_t m_ip;
uint16_t m_port;
#pragma pack(pop)
public:
constexpr ipv4_network_address(uint32_t ip, uint16_t port) noexcept
: m_ip(ip), m_port(port) {}
bool equal(const ipv4_network_address& other) const noexcept;
bool less(const ipv4_network_address& other) const noexcept;
constexpr bool is_same_host(const ipv4_network_address& other) const noexcept
{ return ip() == other.ip(); }
constexpr uint32_t ip() const noexcept { return m_ip; }
constexpr uint16_t port() const noexcept { return m_port; }
std::string str() const;
std::string host_str() const;
bool is_loopback() const;
bool is_local() const;
static constexpr uint8_t get_type_id() noexcept { return ID; }
static const uint8_t ID = 1;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_ip)
KV_SERIALIZE(m_port)
if (!is_store)
const_cast<ipv4_network_address&>(this_ref).init_ids();
END_KV_SERIALIZE_MAP()
};
class network_address: public boost::shared_ptr<network_address_base>
inline bool operator==(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
{ return lhs.equal(rhs); }
inline bool operator!=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
{ return !lhs.equal(rhs); }
inline bool operator<(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
{ return lhs.less(rhs); }
inline bool operator<=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
{ return !rhs.less(lhs); }
inline bool operator>(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
{ return rhs.less(lhs); }
inline bool operator>=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
{ return !lhs.less(rhs); }
class network_address
{
struct interface
{
virtual ~interface() {};
virtual bool equal(const interface&) const = 0;
virtual bool less(const interface&) const = 0;
virtual bool is_same_host(const interface&) const = 0;
virtual std::string str() const = 0;
virtual std::string host_str() const = 0;
virtual bool is_loopback() const = 0;
virtual bool is_local() const = 0;
virtual uint8_t get_type_id() const = 0;
};
template<typename T>
struct implementation final : interface
{
T value;
implementation(const T& src) : value(src) {}
~implementation() = default;
// Type-checks for cast are done in cpp
static const T& cast(const interface& src) noexcept
{ return static_cast<const implementation<T>&>(src).value; }
virtual bool equal(const interface& other) const override
{ return value.equal(cast(other)); }
virtual bool less(const interface& other) const override
{ return value.less(cast(other)); }
virtual bool is_same_host(const interface& other) const override
{ return value.is_same_host(cast(other)); }
virtual std::string str() const override { return value.str(); }
virtual std::string host_str() const override { return value.host_str(); }
virtual bool is_loopback() const override { return value.is_loopback(); }
virtual bool is_local() const override { return value.is_local(); }
virtual uint8_t get_type_id() const override { return value.get_type_id(); }
};
std::shared_ptr<interface> self;
template<typename Type>
Type& as_mutable() const
{
// types `implmentation<Type>` and `implementation<const Type>` are unique
using Type_ = typename std::remove_const<Type>::type;
network_address::interface* const self_ = self.get(); // avoid clang warning in typeid
if (!self_ || typeid(implementation<Type_>) != typeid(*self_))
throw std::bad_cast{};
return static_cast<implementation<Type_>*>(self_)->value;
}
public:
network_address() {}
network_address(ipv4_network_address *address): boost::shared_ptr<network_address_base>(address) {}
bool operator==(const network_address &other) const { return (*this)->operator==(*other); }
bool operator!=(const network_address &other) const { return (*this)->operator!=(*other); }
bool operator<(const network_address &other) const { return (*this)->operator<(*other); }
bool is_same_host(const network_address &other) const { return (*this)->is_same_host(*other); }
std::string str() const { return (*this) ? (*this)->str() : "<none>"; }
std::string host_str() const { return (*this) ? (*this)->host_str() : "<none>"; }
bool is_loopback() const { return (*this)->is_loopback(); }
bool is_local() const { return (*this)->is_local(); }
uint8_t get_type_id() const { return (*this)->get_type_id(); }
template<typename Type> Type &as() { if (get_type_id() != Type::ID) throw std::runtime_error("Bad type"); return *(Type*)get(); }
template<typename Type> const Type &as() const { if (get_type_id() != Type::ID) throw std::runtime_error("Bad type"); return *(const Type*)get(); }
network_address() : self(nullptr) {}
template<typename T>
network_address(const T& src)
: self(std::make_shared<implementation<T>>(src)) {}
bool equal(const network_address &other) const;
bool less(const network_address &other) const;
bool is_same_host(const network_address &other) const;
std::string str() const { return self ? self->str() : "<none>"; }
std::string host_str() const { return self ? self->host_str() : "<none>"; }
bool is_loopback() const { return self ? self->is_loopback() : false; }
bool is_local() const { return self ? self->is_local() : false; }
uint8_t get_type_id() const { return self ? self->get_type_id() : 0; }
template<typename Type> const Type &as() const { return as_mutable<const Type>(); }
BEGIN_KV_SERIALIZE_MAP()
uint8_t type = is_store ? this_ref.get_type_id() : 0;
epee::serialization::selector<is_store>::serialize(type, stg, hparent_section, "type");
if (!epee::serialization::selector<is_store>::serialize(type, stg, hparent_section, "type"))
return false;
switch (type)
{
case ipv4_network_address::ID:
{
if (!is_store)
const_cast<network_address&>(this_ref).reset(new ipv4_network_address(0, 0));
KV_SERIALIZE(template as<ipv4_network_address>());
{
const_cast<network_address&>(this_ref) = ipv4_network_address{0, 0};
auto &addr = this_ref.template as_mutable<ipv4_network_address>();
if (epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "addr"))
MDEBUG("Found as addr: " << this_ref.str());
else if (epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "template as<ipv4_network_address>()"))
MDEBUG("Found as template as<ipv4_network_address>(): " << this_ref.str());
else if (epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "template as_mutable<ipv4_network_address>()"))
MDEBUG("Found as template as_mutable<ipv4_network_address>(): " << this_ref.str());
else
{
MWARNING("Address not found");
return false;
}
}
else
{
auto &addr = this_ref.template as_mutable<ipv4_network_address>();
if (!epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "addr"))
return false;
}
break;
default: MERROR("Unsupported network address type: " << type); return false;
}
default: MERROR("Unsupported network address type: " << (unsigned)type); return false;
}
END_KV_SERIALIZE_MAP()
};
inline bool create_network_address(network_address &address, const std::string &string, uint16_t default_port = 0)
{
uint32_t ip;
uint16_t port;
if (epee::string_tools::parse_peer_from_string(ip, port, string))
{
if (default_port && !port)
port = default_port;
address.reset(new ipv4_network_address(ip, port));
return true;
}
return false;
}
inline bool operator==(const network_address& lhs, const network_address& rhs)
{ return lhs.equal(rhs); }
inline bool operator!=(const network_address& lhs, const network_address& rhs)
{ return !lhs.equal(rhs); }
inline bool operator<(const network_address& lhs, const network_address& rhs)
{ return lhs.less(rhs); }
inline bool operator<=(const network_address& lhs, const network_address& rhs)
{ return !rhs.less(lhs); }
inline bool operator>(const network_address& lhs, const network_address& rhs)
{ return rhs.less(lhs); }
inline bool operator>=(const network_address& lhs, const network_address& rhs)
{ return !lhs.less(rhs); }
bool create_network_address(network_address &address, const std::string &string, uint16_t default_port = 0);
/************************************************************************/
/* */
/************************************************************************/
@ -179,7 +246,7 @@ namespace net_utils
{}
connection_context_base(): m_connection_id(),
m_remote_address(new ipv4_network_address(0,0)),
m_remote_address(ipv4_network_address{0,0}),
m_is_income(false),
m_started(time(NULL)),
m_last_recv(0),
@ -228,21 +295,8 @@ namespace net_utils
//some helpers
inline
std::string print_connection_context(const connection_context_base& ctx)
{
std::stringstream ss;
ss << ctx.m_remote_address->str() << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT");
return ss.str();
}
inline
std::string print_connection_context_short(const connection_context_base& ctx)
{
std::stringstream ss;
ss << ctx.m_remote_address->str() << (ctx.m_is_income ? " INC":" OUT");
return ss.str();
}
std::string print_connection_context(const connection_context_base& ctx);
std::string print_connection_context_short(const connection_context_base& ctx);
inline MAKE_LOGGABLE(connection_context_base, ct, os)
{

View File

@ -0,0 +1,125 @@
/// @file
/// @author rfree (current maintainer in monero.cc project)
/// @brief implementaion for throttling of connection (count and rate-limit speed etc)
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* rfree: throttle details, implementing rate limiting */
#ifndef INCLUDED_throttle_detail_hpp
#define INCLUDED_throttle_detail_hpp
#include "network_throttle.hpp"
namespace epee
{
namespace net_utils
{
class network_throttle : public i_network_throttle {
private:
struct packet_info {
size_t m_size; // octets sent. Summary for given small-window (e.g. for all packaged in 1 second)
packet_info();
};
network_speed_bps m_target_speed;
size_t m_network_add_cost; // estimated add cost of headers
size_t m_network_minimal_segment; // estimated minimal cost of sending 1 byte to round up to
size_t m_network_max_segment; // recommended max size of 1 TCP transmission
const size_t m_window_size; // the number of samples to average over
network_time_seconds m_slot_size; // the size of one slot. TODO: now hardcoded for 1 second e.g. in time_to_slot()
// TODO for big window size, for performance better the substract on change of m_last_sample_time instead of recalculating average of eg >100 elements
std::vector< packet_info > m_history; // the history of bw usage
network_time_seconds m_last_sample_time; // time of last history[0] - so we know when to rotate the buffer
network_time_seconds m_start_time; // when we were created
bool m_any_packet_yet; // did we yet got any packet to count
std::string m_name; // my name for debug and logs
std::string m_nameshort; // my name for debug and logs (used in log file name)
// each sample is now 1 second
public:
network_throttle(const std::string &nameshort, const std::string &name, int window_size=-1);
virtual ~network_throttle();
virtual void set_name(const std::string &name);
virtual void set_target_speed( network_speed_kbps target );
virtual network_speed_kbps get_target_speed();
// add information about events:
virtual void handle_trafic_exact(size_t packet_size); ///< count the new traffic/packet; the size is exact considering all network costs
virtual void handle_trafic_tcp(size_t packet_size); ///< count the new traffic/packet; the size is as TCP, we will consider MTU etc
virtual void tick(); ///< poke and update timers/history (recalculates, moves the history if needed, checks the real clock etc)
virtual double get_time_seconds() const ; ///< timer that we use, time in seconds, monotionic
// time calculations:
virtual void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const; ///< MAIN LOGIC (see base class for info)
virtual network_time_seconds get_sleep_time_after_tick(size_t packet_size); ///< increase the timer if needed, and get the package size
virtual network_time_seconds get_sleep_time(size_t packet_size) const; ///< gets the Delay (recommended Delay time) from calc. (not safe: only if time didnt change?) TODO
virtual size_t get_recommended_size_of_planned_transport() const; ///< what should be the size (bytes) of next data block to be transported
virtual size_t get_recommended_size_of_planned_transport_window(double force_window) const; ///< ditto, but for given windows time frame
virtual double get_current_speed() const;
private:
virtual network_time_seconds time_to_slot(network_time_seconds t) const { return std::floor( t ); } // convert exact time eg 13.7 to rounded time for slot number in history 13
virtual void _handle_trafic_exact(size_t packet_size, size_t orginal_size);
virtual void logger_handle_net(const std::string &filename, double time, size_t size);
};
/***
* The complete set of traffic throttle for one typical connection
*/
struct network_throttle_bw {
public:
network_throttle m_in; ///< for incomming traffic (this we can not controll directly as it depends of what others send to us - usually)
network_throttle m_inreq; ///< for requesting incomming traffic (this is exact usually)
network_throttle m_out; ///< for outgoing traffic that we just sent (this is exact usually)
public:
network_throttle_bw(const std::string &name1);
};
} // namespace net_utils
} // namespace epee
#endif

View File

@ -0,0 +1,171 @@
/// @file
/// @author rfree (current maintainer in monero.cc project)
/// @brief interface for throttling of connection (count and rate-limit speed etc)
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* rfree: throttle basic interface */
/* rfree: also includes the manager for singeton/global such objects */
#ifndef INCLUDED_network_throttle_hpp
#define INCLUDED_network_throttle_hpp
#include <boost/asio.hpp>
#include <string>
#include <vector>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <atomic>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/interprocess/detail/atomic.hpp>
#include <boost/thread/thread.hpp>
#include "syncobj.h"
#include "net/net_utils_base.h"
#include "misc_log_ex.h"
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/chrono.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
#include "misc_language.h"
#include "pragma_comp_defs.h"
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <memory>
#include <mutex>
#include <fstream>
namespace epee
{
namespace net_utils
{
// just typedefs to in code define the units used. TODO later it will be enforced that casts to other numericals are only explicit to avoid mistakes? use boost::chrono?
typedef double network_speed_kbps; // externally, for parameters and return values, all defined in kilobytes per second
typedef double network_speed_bps; // throttle-internally, bytes per second
typedef double network_time_seconds;
typedef double network_MB;
class i_network_throttle;
/***
@brief All information about given throttle - speed calculations
*/
struct calculate_times_struct {
double average;
double window;
double delay;
double recomendetDataSize;
};
typedef calculate_times_struct calculate_times_struct;
/***
@brief Access to simple throttles, with singlton to access global network limits
*/
class network_throttle_manager {
// provides global (singleton) in/inreq/out throttle access
// [[note1]] see also http://www.nuonsoft.com/blog/2012/10/21/implementing-a-thread-safe-singleton-with-c11/
// [[note2]] _inreq is the requested in traffic - we anticipate we will get in-bound traffic soon as result of what we do (e.g. that we sent network downloads requests)
//protected:
public: // XXX
static boost::mutex m_lock_get_global_throttle_in;
static boost::mutex m_lock_get_global_throttle_inreq;
static boost::mutex m_lock_get_global_throttle_out;
friend class connection_basic; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS!
friend class connection_basic_pimpl; // ditto
public:
static i_network_throttle & get_global_throttle_in(); ///< singleton ; for friend class ; caller MUST use proper locks! like m_lock_get_global_throttle_in
static i_network_throttle & get_global_throttle_inreq(); ///< ditto ; use lock ... use m_lock_get_global_throttle_inreq obviously
static i_network_throttle & get_global_throttle_out(); ///< ditto ; use lock ... use m_lock_get_global_throttle_out obviously
};
/***
@brief interface for the throttle, see the derivated class
*/
class i_network_throttle {
public:
virtual void set_name(const std::string &name)=0;
virtual void set_target_speed( network_speed_kbps target )=0;
virtual network_speed_kbps get_target_speed()=0;
virtual void handle_trafic_exact(size_t packet_size) =0; // count the new traffic/packet; the size is exact considering all network costs
virtual void handle_trafic_tcp(size_t packet_size) =0; // count the new traffic/packet; the size is as TCP, we will consider MTU etc
virtual void tick() =0; // poke and update timers/history
// time calculations:
virtual void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const =0; // assuming sending new package (or 0), calculate:
// Average, Window, Delay, Recommended data size ; also gets dbg=debug flag, and forced widnow size if >0 or -1 for not forcing window size
// Average speed, Window size, recommended Delay to sleep now, Recommended size of data to send now
virtual network_time_seconds get_sleep_time(size_t packet_size) const =0; // gets the D (recommended Delay time) from calc
virtual network_time_seconds get_sleep_time_after_tick(size_t packet_size) =0; // ditto, but first tick the timer
virtual size_t get_recommended_size_of_planned_transport() const =0; // what should be the recommended limit of data size that we can transport over current network_throttle in near future
virtual double get_time_seconds() const =0; // a timer
virtual void logger_handle_net(const std::string &filename, double time, size_t size)=0;
};
// ... more in the -advanced.h file
} // namespace net_utils
} // namespace epee
#endif

View File

@ -28,6 +28,8 @@
#ifndef _PROFILE_TOOLS_H_
#define _PROFILE_TOOLS_H_
#include "misc_os_dependent.h"
namespace epee
{

View File

@ -2,9 +2,7 @@
#include <streambuf>
#include <sstream>
#include <iostream>
#include <vector>
#include <algorithm>
namespace rdln
{

View File

@ -29,7 +29,7 @@
#define _REG_EXP_DEFINER_H_
#include <boost/interprocess/detail/atomic.hpp>
#include "syncobj.h"
namespace epee
{

View File

@ -31,7 +31,6 @@
#include "misc_log_ex.h"
#include "enableable.h"
#include "keyvalue_serialization_overloads.h"
#include "serialization/serialization.h"
namespace epee
{

View File

@ -26,6 +26,13 @@
#pragma once
#include <set>
#include <list>
#include <vector>
#include <deque>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains_fwd.hpp>
namespace epee
{
namespace serialization
@ -73,7 +80,7 @@ namespace epee
template<class serializible_type, class t_storage>
static bool unserialize_t_obj(serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true);
typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, false);
if(!hchild_section) return false;
return obj._load(stg, hchild_section);
}
@ -90,7 +97,7 @@ namespace epee
static bool unserialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
obj.enabled = false;
typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true);
typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, false);
if(!hchild_section) return false;
obj.enabled = true;
return obj.v._load(stg, hchild_section);
@ -117,9 +124,9 @@ namespace epee
typename stl_container::value_type exchange_val;
typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section);
if(!hval_array) return false;
container.push_back(std::move(exchange_val));
container.insert(container.end(), std::move(exchange_val));
while(stg.get_next_value(hval_array, exchange_val))
container.push_back(std::move(exchange_val));
container.insert(container.end(), std::move(exchange_val));
return true;
}//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
@ -152,7 +159,7 @@ namespace epee
"size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type));
size_t count = (loaded_size/sizeof(typename stl_container::value_type));
for(size_t i = 0; i < count; i++)
container.push_back(*(pelem++));
container.insert(container.end(), *(pelem++));
}
return res;
}
@ -186,12 +193,12 @@ namespace epee
typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section);
if(!hsec_array || !hchild_section) return false;
res = val._load(stg, hchild_section);
container.push_back(val);
container.insert(container.end(), val);
while(stg.get_next_section(hsec_array, hchild_section))
{
typename stl_container::value_type val_l = typename stl_container::value_type();
res |= val_l._load(stg, hchild_section);
container.push_back(std::move(val_l));
container.insert(container.end(), std::move(val_l));
}
return res;
}
@ -227,6 +234,18 @@ namespace epee
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_unserialize(std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return unserialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_val(d, stg, hparent_section, pname);
@ -238,6 +257,18 @@ namespace epee
return unserialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return unserialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
};
template<>
struct kv_serialization_overloads_impl_is_base_serializable_types<false>
@ -268,6 +299,18 @@ namespace epee
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_unserialize(std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return unserialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_obj(d, stg, hparent_section, pname);
@ -278,6 +321,18 @@ namespace epee
{
return unserialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return unserialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
};
template<class t_storage>
struct base_serializable_types: public boost::mpl::vector<uint64_t, uint32_t, uint16_t, uint8_t, int64_t, int32_t, int16_t, int8_t, double, bool, std::string, typename t_storage::meta_entry>::type
@ -353,6 +408,18 @@ namespace epee
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool kv_serialize(const std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool kv_unserialize(std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname);
@ -363,5 +430,17 @@ namespace epee
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017, The Monero Project
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
@ -108,7 +108,7 @@ namespace epee
template<typename T>
constexpr bool has_padding() noexcept
{
return !std::is_pod<T>() || alignof(T) != 1;
return !std::is_standard_layout<T>() || alignof(T) != 1;
}
//! \return Cast data from `src` as `span<const std::uint8_t>`.

View File

@ -44,8 +44,11 @@ namespace epee
if(!serialization::store_t_to_json(out_struct, req_param))
return false;
http::fields_list additional_params;
additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8"));
const http::http_response_info* pri = NULL;
if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri)))
if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri), std::move(additional_params)))
{
LOG_PRINT_L1("Failed to invoke http request to " << uri);
return false;
@ -112,7 +115,7 @@ namespace epee
}
if(resp_t.error.code || resp_t.error.message.size())
{
LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message);
LOG_ERROR("RPC call of \"" << req_t.method << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message);
return false;
}
result_struct = resp_t.result;

View File

@ -60,8 +60,7 @@ namespace epee
LOG_ERROR("Failed to load_from_binary on command " << command);
return false;
}
result_struct.load(stg_ret);
return true;
return result_struct.load(stg_ret);
}
template<class t_arg, class t_transport>
@ -105,13 +104,11 @@ namespace epee
LOG_ERROR("Failed to load_from_binary on command " << command);
return false;
}
result_struct.load(stg_ret);
return true;
return result_struct.load(stg_ret);
}
template<class t_result, class t_arg, class callback_t, class t_transport>
bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, callback_t cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
{
typename serialization::portable_storage stg;
const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation
@ -133,7 +130,12 @@ namespace epee
cb(LEVIN_ERROR_FORMAT, result_struct, context);
return false;
}
result_struct.load(stg_ret);
if (!result_struct.load(stg_ret))
{
LOG_ERROR("Failed to load result struct on command " << command);
cb(LEVIN_ERROR_FORMAT, result_struct, context);
return false;
}
cb(code, result_struct, context);
return true;
}, inv_timeout);
@ -176,7 +178,11 @@ namespace epee
boost::value_initialized<t_in_type> in_struct;
boost::value_initialized<t_out_type> out_struct;
static_cast<t_in_type&>(in_struct).load(strg);
if (!static_cast<t_in_type&>(in_struct).load(strg))
{
LOG_ERROR("Failed to load in_struct in command " << command);
return -1;
}
int res = cb(command, static_cast<t_in_type&>(in_struct), static_cast<t_out_type&>(out_struct), context);
serialization::portable_storage strg_out;
static_cast<t_out_type&>(out_struct).store(strg_out);
@ -200,7 +206,11 @@ namespace epee
return -1;
}
boost::value_initialized<t_in_type> in_struct;
static_cast<t_in_type&>(in_struct).load(strg);
if (!static_cast<t_in_type&>(in_struct).load(strg))
{
LOG_ERROR("Failed to load in_struct in notify " << command);
return -1;
}
return cb(command, in_struct, context);
}

View File

@ -25,6 +25,8 @@
//
#pragma once
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include "parserse_base_utils.h"
#include "file_io_utils.h"

View File

@ -28,6 +28,7 @@
#pragma once
#include "pragma_comp_defs.h"
#include "misc_language.h"
#include "portable_storage_base.h"
@ -47,6 +48,9 @@ namespace epee
PRAGMA_WARNING_PUSH
PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"")
#ifdef __clang__
PRAGMA_GCC("GCC diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"")
#endif
template<class t_stream>
size_t pack_varint(t_stream& strm, size_t val)
{ //the first two bits always reserved for size information

View File

@ -28,6 +28,9 @@
#pragma once
#include <time.h>
#include <boost/regex.hpp>
#include "misc_language.h"
#include "portable_storage_base.h"
#include "warnings.h"
@ -131,6 +134,36 @@ POP_WARNINGS
}
};
// For MyMonero/OpenMonero backend compatibility
// MyMonero backend sends amount, fees and timestamp values as strings.
// Until MM backend is updated, this is needed for compatibility between OpenMonero and MyMonero.
template<>
struct convert_to_integral<std::string, uint64_t, false>
{
static void convert(const std::string& from, uint64_t& to)
{
MTRACE("Converting std::string to uint64_t. Source: " << from);
// String only contains digits
if(std::all_of(from.begin(), from.end(), ::isdigit))
to = boost::lexical_cast<uint64_t>(from);
// MyMonero ISO 8061 timestamp (2017-05-06T16:27:06Z)
else if (boost::regex_match (from, boost::regex("\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\dZ")))
{
// Convert to unix timestamp
#ifdef HAVE_STRPTIME
struct tm tm;
if (strptime(from.c_str(), "%Y-%m-%dT%H:%M:%S", &tm))
#else
std::tm tm = {};
std::istringstream ss(from);
if (ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"))
#endif
to = std::mktime(&tm);
} else
ASSERT_AND_THROW_WRONG_CONVERSION();
}
};
template<class from_type, class to_type>
struct is_convertable: std::integral_constant<bool,
std::is_integral<to_type>::value &&

View File

@ -35,6 +35,7 @@
# include <windows.h>
#endif
#include <string.h>
#include <locale>
#include <cstdlib>
#include <string>
@ -42,8 +43,9 @@
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include "hex.h"
#include "memwipe.h"
#include "span.h"
#include "warnings.h"
@ -160,7 +162,7 @@ DISABLE_GCC_WARNING(maybe-uninitialized)
val = boost::lexical_cast<XType>(str_id);
return true;
}
catch(std::exception& /*e*/)
catch(const std::exception& /*e*/)
{
//const char* pmsg = e.what();
return false;
@ -329,7 +331,7 @@ POP_WARNINGS
template<class t_pod_type>
std::string pod_to_hex(const t_pod_type& s)
{
static_assert(std::is_pod<t_pod_type>::value, "expected pod type");
static_assert(std::is_standard_layout<t_pod_type>(), "expected standard layout type");
return to_hex::string(as_byte_span(s));
}
//----------------------------------------------------------------------------
@ -349,6 +351,14 @@ POP_WARNINGS
s = *(t_pod_type*)bin_buff.data();
return true;
}
//----------------------------------------------------------------------------
template<class t_pod_type>
bool hex_to_pod(const std::string& hex_str, tools::scrubbed<t_pod_type>& s)
{
return hex_to_pod(hex_str, unwrap(s));
}
//----------------------------------------------------------------------------
bool validate_hex(uint64_t length, const std::string& str);
//----------------------------------------------------------------------------
inline std::string get_extension(const std::string& str)
{

View File

@ -0,0 +1,67 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <stddef.h>
#include <vector>
#include <string>
namespace epee
{
class wipeable_string
{
public:
wipeable_string() {}
wipeable_string(const wipeable_string &other);
wipeable_string(wipeable_string &&other);
wipeable_string(const std::string &other);
wipeable_string(std::string &&other);
wipeable_string(const char *s);
~wipeable_string();
void wipe();
void push_back(char c);
void pop_back();
const char *data() const noexcept { return buffer.data(); }
size_t size() const noexcept { return buffer.size(); }
bool empty() const noexcept { return buffer.empty(); }
void resize(size_t sz);
void reserve(size_t sz);
void clear();
bool operator==(const wipeable_string &other) const noexcept { return buffer == other.buffer; }
bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; }
wipeable_string &operator=(wipeable_string &&other);
wipeable_string &operator=(const wipeable_string &other);
private:
void grow(size_t sz, size_t reserved = 0);
private:
std::vector<char> buffer;
};
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017, The Monero Project
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//

View File

@ -0,0 +1,116 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file Copyright (c) 2009-2015 The Bitcoin Core developers
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#ifdef HAVE_EXPLICIT_BZERO
#include <strings.h>
#endif
#include "memwipe.h"
#if defined(_MSC_VER)
#define SCARECROW
#else
#define SCARECROW \
__asm__ __volatile__("" : : "r"(ptr) : "memory");
#endif
#ifdef HAVE_MEMSET_S
void *memwipe(void *ptr, size_t n)
{
if (memset_s(ptr, n, 0, n))
{
#ifdef NDEBUG
fprintf(stderr, "Error: memset_s failed\n");
_exit(1);
#else
abort();
#endif
}
SCARECROW // might as well...
return ptr;
}
#elif defined HAVE_EXPLICIT_BZERO
void *memwipe(void *ptr, size_t n)
{
explicit_bzero(ptr, n);
SCARECROW
return ptr;
}
#else
/* The memory_cleanse implementation is taken from Bitcoin */
/* Compilers have a bad habit of removing "superfluous" memset calls that
* are trying to zero memory. For example, when memset()ing a buffer and
* then free()ing it, the compiler might decide that the memset is
* unobservable and thus can be removed.
*
* Previously we used OpenSSL which tried to stop this by a) implementing
* memset in assembly on x86 and b) putting the function in its own file
* for other platforms.
*
* This change removes those tricks in favour of using asm directives to
* scare the compiler away. As best as our compiler folks can tell, this is
* sufficient and will continue to be so.
*
* Adam Langley <agl@google.com>
* Commit: ad1907fe73334d6c696c8539646c21b11178f20f
* BoringSSL (LICENSE: ISC)
*/
static void memory_cleanse(void *ptr, size_t len)
{
memset(ptr, 0, len);
/* As best as we can tell, this is sufficient to break any optimisations that
might try to eliminate "superfluous" memsets. If there's an easy way to
detect memset_s, it would be better to use that. */
SCARECROW
}
void *memwipe(void *ptr, size_t n)
{
memory_cleanse(ptr, n);
SCARECROW
return ptr;
}
#endif

View File

@ -0,0 +1,146 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string.h>
#include "memwipe.h"
#include "misc_log_ex.h"
#include "wipeable_string.h"
namespace epee
{
wipeable_string::wipeable_string(const wipeable_string &other):
buffer(other.buffer)
{
}
wipeable_string::wipeable_string(wipeable_string &&other)
{
if (&other == this)
return;
buffer = std::move(other.buffer);
}
wipeable_string::wipeable_string(const std::string &other)
{
grow(other.size());
memcpy(buffer.data(), other.c_str(), size());
}
wipeable_string::wipeable_string(std::string &&other)
{
grow(other.size());
memcpy(buffer.data(), other.c_str(), size());
if (!other.empty())
{
memwipe(&other[0], other.size()); // we're kinda left with this again aren't we
other = std::string();
}
}
wipeable_string::wipeable_string(const char *s)
{
grow(strlen(s));
memcpy(buffer.data(), s, size());
}
wipeable_string::~wipeable_string()
{
wipe();
}
void wipeable_string::wipe()
{
if (!buffer.empty())
memwipe(buffer.data(), buffer.size() * sizeof(char));
}
void wipeable_string::grow(size_t sz, size_t reserved)
{
if (reserved < sz)
reserved = sz;
if (reserved <= buffer.capacity())
{
if (sz < buffer.size())
memwipe(buffer.data() + sz, buffer.size() - sz);
buffer.resize(sz);
return;
}
size_t old_sz = buffer.size();
std::unique_ptr<char[]> tmp{new char[old_sz]};
memcpy(tmp.get(), buffer.data(), old_sz * sizeof(char));
if (old_sz > 0)
memwipe(buffer.data(), old_sz * sizeof(char));
buffer.reserve(reserved);
buffer.resize(sz);
memcpy(buffer.data(), tmp.get(), old_sz * sizeof(char));
if (old_sz > 0)
memwipe(tmp.get(), old_sz * sizeof(char));
}
void wipeable_string::push_back(char c)
{
grow(size() + 1);
buffer.back() = c;
}
void wipeable_string::pop_back()
{
resize(size() - 1);
}
void wipeable_string::resize(size_t sz)
{
grow(sz);
}
void wipeable_string::reserve(size_t sz)
{
grow(size(), sz);
}
void wipeable_string::clear()
{
resize(0);
}
wipeable_string &wipeable_string::operator=(wipeable_string &&other)
{
if (&other != this)
buffer = std::move(other.buffer);
return *this;
}
wipeable_string &wipeable_string::operator=(const wipeable_string &other)
{
if (&other != this)
buffer = other.buffer;
return *this;
}
}

View File

@ -0,0 +1,103 @@
# Copyright (c) 2014-2018, The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(crypto_sources
aesb.c
blake256.c
chacha.c
crypto-ops-data.c
crypto-ops.c
crypto.cpp
groestl.c
hash-extra-blake.c
hash-extra-groestl.c
hash-extra-jh.c
hash-extra-skein.c
hash.c
jh.c
keccak.c
oaes_lib.c
random.c
skein.c
slow-hash.c
tree-hash.c)
set(crypto_headers)
set(crypto_private_headers
blake256.h
chacha.h
crypto-ops.h
crypto.h
generic-ops.h
groestl.h
groestl_tables.h
hash-ops.h
hash.h
initializer.h
jh.h
keccak.h
oaes_config.h
oaes_lib.h
random.h
skein.h
skein_port.h)
monero_private_headers(cncrypto
${crypto_private_headers})
monero_add_library(cncrypto
${crypto_sources}
${crypto_headers}
${crypto_private_headers})
target_link_libraries(cncrypto
PUBLIC
epee
${Boost_SYSTEM_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
if (ARM)
option(NO_OPTIMIZED_MULTIPLY_ON_ARM
"Compute multiply using generic C implementation instead of ARM ASM" OFF)
if(NO_OPTIMIZED_MULTIPLY_ON_ARM)
message(STATUS "Using generic C implementation for multiply")
set_property(SOURCE slow-hash.c
PROPERTY COMPILE_DEFINITIONS "NO_OPTIMIZED_MULTIPLY_ON_ARM")
endif()
endif()
# Because of the way Qt works on android with JNI, the code does not live in the main android thread
# So this code runs with a 1 MB default stack size.
# This will force the use of the heap for the allocation of the scratchpad
if (ANDROID OR IOS)
if( BUILD_GUI_DEPS )
add_definitions(-DFORCE_USE_HEAP=1)
endif()
endif()

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -157,7 +157,7 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) {
int left = S->buflen >> 3;
int fill = 64 - left;
if (left && (((datalen >> 3) & 0x3F) >= (unsigned) fill)) {
if (left && (((datalen >> 3)) >= (unsigned) fill)) {
memcpy((void *) (S->buf + left), (void *) data, fill);
S->t[0] += 512;
if (S->t[0] == 0) S->t[1]++;

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//

View File

@ -0,0 +1,182 @@
/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
#include <memory.h>
#include <stdio.h>
#ifndef _MSC_VER
#include <sys/param.h>
#endif
#include "chacha.h"
#include "common/int-util.h"
#include "warnings.h"
/*
* The following macros are used to obtain exact-width results.
*/
#define U8V(v) ((uint8_t)(v) & UINT8_C(0xFF))
#define U32V(v) ((uint32_t)(v) & UINT32_C(0xFFFFFFFF))
/*
* The following macros load words from an array of bytes with
* different types of endianness, and vice versa.
*/
#define U8TO32_LITTLE(p) SWAP32LE(((uint32_t*)(p))[0])
#define U32TO8_LITTLE(p, v) (((uint32_t*)(p))[0] = SWAP32LE(v))
#define ROTATE(v,c) (rol32(v,c))
#define XOR(v,w) ((v) ^ (w))
#define PLUS(v,w) (U32V((v) + (w)))
#define PLUSONE(v) (PLUS((v),1))
#define QUARTERROUND(a,b,c,d) \
a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
static const char sigma[] = "expand 32-byte k";
DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing)
static void chacha(unsigned rounds, const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) {
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
char* ctarget = 0;
char tmp[64];
int i;
if (!length) return;
j0 = U8TO32_LITTLE(sigma + 0);
j1 = U8TO32_LITTLE(sigma + 4);
j2 = U8TO32_LITTLE(sigma + 8);
j3 = U8TO32_LITTLE(sigma + 12);
j4 = U8TO32_LITTLE(key + 0);
j5 = U8TO32_LITTLE(key + 4);
j6 = U8TO32_LITTLE(key + 8);
j7 = U8TO32_LITTLE(key + 12);
j8 = U8TO32_LITTLE(key + 16);
j9 = U8TO32_LITTLE(key + 20);
j10 = U8TO32_LITTLE(key + 24);
j11 = U8TO32_LITTLE(key + 28);
j12 = 0;
j13 = 0;
j14 = U8TO32_LITTLE(iv + 0);
j15 = U8TO32_LITTLE(iv + 4);
for (;;) {
if (length < 64) {
memcpy(tmp, data, length);
data = tmp;
ctarget = cipher;
cipher = tmp;
}
x0 = j0;
x1 = j1;
x2 = j2;
x3 = j3;
x4 = j4;
x5 = j5;
x6 = j6;
x7 = j7;
x8 = j8;
x9 = j9;
x10 = j10;
x11 = j11;
x12 = j12;
x13 = j13;
x14 = j14;
x15 = j15;
for (i = rounds;i > 0;i -= 2) {
QUARTERROUND( x0, x4, x8,x12)
QUARTERROUND( x1, x5, x9,x13)
QUARTERROUND( x2, x6,x10,x14)
QUARTERROUND( x3, x7,x11,x15)
QUARTERROUND( x0, x5,x10,x15)
QUARTERROUND( x1, x6,x11,x12)
QUARTERROUND( x2, x7, x8,x13)
QUARTERROUND( x3, x4, x9,x14)
}
x0 = PLUS( x0, j0);
x1 = PLUS( x1, j1);
x2 = PLUS( x2, j2);
x3 = PLUS( x3, j3);
x4 = PLUS( x4, j4);
x5 = PLUS( x5, j5);
x6 = PLUS( x6, j6);
x7 = PLUS( x7, j7);
x8 = PLUS( x8, j8);
x9 = PLUS( x9, j9);
x10 = PLUS(x10,j10);
x11 = PLUS(x11,j11);
x12 = PLUS(x12,j12);
x13 = PLUS(x13,j13);
x14 = PLUS(x14,j14);
x15 = PLUS(x15,j15);
x0 = XOR( x0,U8TO32_LITTLE((uint8_t*)data + 0));
x1 = XOR( x1,U8TO32_LITTLE((uint8_t*)data + 4));
x2 = XOR( x2,U8TO32_LITTLE((uint8_t*)data + 8));
x3 = XOR( x3,U8TO32_LITTLE((uint8_t*)data + 12));
x4 = XOR( x4,U8TO32_LITTLE((uint8_t*)data + 16));
x5 = XOR( x5,U8TO32_LITTLE((uint8_t*)data + 20));
x6 = XOR( x6,U8TO32_LITTLE((uint8_t*)data + 24));
x7 = XOR( x7,U8TO32_LITTLE((uint8_t*)data + 28));
x8 = XOR( x8,U8TO32_LITTLE((uint8_t*)data + 32));
x9 = XOR( x9,U8TO32_LITTLE((uint8_t*)data + 36));
x10 = XOR(x10,U8TO32_LITTLE((uint8_t*)data + 40));
x11 = XOR(x11,U8TO32_LITTLE((uint8_t*)data + 44));
x12 = XOR(x12,U8TO32_LITTLE((uint8_t*)data + 48));
x13 = XOR(x13,U8TO32_LITTLE((uint8_t*)data + 52));
x14 = XOR(x14,U8TO32_LITTLE((uint8_t*)data + 56));
x15 = XOR(x15,U8TO32_LITTLE((uint8_t*)data + 60));
j12 = PLUSONE(j12);
if (!j12)
{
j13 = PLUSONE(j13);
/* stopping at 2^70 bytes per iv is user's responsibility */
}
U32TO8_LITTLE(cipher + 0,x0);
U32TO8_LITTLE(cipher + 4,x1);
U32TO8_LITTLE(cipher + 8,x2);
U32TO8_LITTLE(cipher + 12,x3);
U32TO8_LITTLE(cipher + 16,x4);
U32TO8_LITTLE(cipher + 20,x5);
U32TO8_LITTLE(cipher + 24,x6);
U32TO8_LITTLE(cipher + 28,x7);
U32TO8_LITTLE(cipher + 32,x8);
U32TO8_LITTLE(cipher + 36,x9);
U32TO8_LITTLE(cipher + 40,x10);
U32TO8_LITTLE(cipher + 44,x11);
U32TO8_LITTLE(cipher + 48,x12);
U32TO8_LITTLE(cipher + 52,x13);
U32TO8_LITTLE(cipher + 56,x14);
U32TO8_LITTLE(cipher + 60,x15);
if (length <= 64) {
if (length < 64) {
memcpy(ctarget, cipher, length);
}
return;
}
length -= 64;
cipher += 64;
data = (uint8_t*)data + 64;
}
}
void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher)
{
chacha(8, data, length, key, iv, cipher);
}
void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher)
{
chacha(20, data, length, key, iv, cipher);
}

View File

@ -0,0 +1,91 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <stdint.h>
#include <stddef.h>
#define CHACHA_KEY_SIZE 32
#define CHACHA_IV_SIZE 8
#if defined(__cplusplus)
#include <memory.h>
#include "memwipe.h"
#include "hash.h"
namespace crypto {
extern "C" {
#endif
void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher);
void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher);
#if defined(__cplusplus)
}
using chacha_key = tools::scrubbed_arr<uint8_t, CHACHA_KEY_SIZE>;
#pragma pack(push, 1)
// MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct
struct chacha_iv {
uint8_t data[CHACHA_IV_SIZE];
};
#pragma pack(pop)
static_assert(sizeof(chacha_key) == CHACHA_KEY_SIZE && sizeof(chacha_iv) == CHACHA_IV_SIZE, "Invalid structure size");
inline void chacha8(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) {
chacha8(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
}
inline void chacha20(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) {
chacha20(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
}
inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
memcpy(&key, pwd_hash.data(), sizeof(key));
}
inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/);
memcpy(&key, pwd_hash.data(), sizeof(key));
}
inline void generate_chacha_key(std::string password, chacha_key& key) {
return generate_chacha_key(password.data(), password.size(), key);
}
}
#endif

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -870,3 +870,4 @@ const fe fe_fffb1 = {-31702527, -2466483, -26106795, -12203692, -12169197, -3210
const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -30876704, -6368709, 10503587, -13363080}; /* sqrt(2 * A * (A + 2)) */
const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */
const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */
const ge_p3 ge_p3_identity = { {0}, {1, 0}, {1, 0}, {0} };

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -1234,6 +1234,51 @@ void ge_double_scalarmult_base_vartime(ge_p2 *r, const unsigned char *a, const g
}
}
void ge_double_scalarmult_base_vartime_p3(ge_p3 *r3, const unsigned char *a, const ge_p3 *A, const unsigned char *b) {
signed char aslide[256];
signed char bslide[256];
ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */
ge_p1p1 t;
ge_p3 u;
ge_p2 r;
int i;
slide(aslide, a);
slide(bslide, b);
ge_dsm_precomp(Ai, A);
ge_p2_0(&r);
for (i = 255; i >= 0; --i) {
if (aslide[i] || bslide[i]) break;
}
for (; i >= 0; --i) {
ge_p2_dbl(&t, &r);
if (aslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_add(&t, &u, &Ai[aslide[i]/2]);
} else if (aslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_sub(&t, &u, &Ai[(-aslide[i])/2]);
}
if (bslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_madd(&t, &u, &ge_Bi[bslide[i]/2]);
} else if (bslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_msub(&t, &u, &ge_Bi[(-bslide[i])/2]);
}
if (i == 0)
ge_p1p1_to_p3(r3, &t);
else
ge_p1p1_to_p2(&r, &t);
}
}
/* From ge_frombytes.c, modified */
int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) {
@ -2000,17 +2045,79 @@ void ge_scalarmult(ge_p2 *r, const unsigned char *a, const ge_p3 *A) {
}
}
void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) {
void ge_scalarmult_p3(ge_p3 *r3, const unsigned char *a, const ge_p3 *A) {
signed char e[64];
int carry, carry2, i;
ge_cached Ai[8]; /* 1 * A, 2 * A, ..., 8 * A */
ge_p1p1 t;
ge_p3 u;
ge_p2 r;
carry = 0; /* 0..1 */
for (i = 0; i < 31; i++) {
carry += a[i]; /* 0..256 */
carry2 = (carry + 8) >> 4; /* 0..16 */
e[2 * i] = carry - (carry2 << 4); /* -8..7 */
carry = (carry2 + 8) >> 4; /* 0..1 */
e[2 * i + 1] = carry2 - (carry << 4); /* -8..7 */
}
carry += a[31]; /* 0..128 */
carry2 = (carry + 8) >> 4; /* 0..8 */
e[62] = carry - (carry2 << 4); /* -8..7 */
e[63] = carry2; /* 0..8 */
ge_p3_to_cached(&Ai[0], A);
for (i = 0; i < 7; i++) {
ge_add(&t, A, &Ai[i]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[i + 1], &u);
}
ge_p2_0(&r);
for (i = 63; i >= 0; i--) {
signed char b = e[i];
unsigned char bnegative = negative(b);
unsigned char babs = b - (((-bnegative) & b) << 1);
ge_cached cur, minuscur;
ge_p2_dbl(&t, &r);
ge_p1p1_to_p2(&r, &t);
ge_p2_dbl(&t, &r);
ge_p1p1_to_p2(&r, &t);
ge_p2_dbl(&t, &r);
ge_p1p1_to_p2(&r, &t);
ge_p2_dbl(&t, &r);
ge_p1p1_to_p3(&u, &t);
ge_cached_0(&cur);
ge_cached_cmov(&cur, &Ai[0], equal(babs, 1));
ge_cached_cmov(&cur, &Ai[1], equal(babs, 2));
ge_cached_cmov(&cur, &Ai[2], equal(babs, 3));
ge_cached_cmov(&cur, &Ai[3], equal(babs, 4));
ge_cached_cmov(&cur, &Ai[4], equal(babs, 5));
ge_cached_cmov(&cur, &Ai[5], equal(babs, 6));
ge_cached_cmov(&cur, &Ai[6], equal(babs, 7));
ge_cached_cmov(&cur, &Ai[7], equal(babs, 8));
fe_copy(minuscur.YplusX, cur.YminusX);
fe_copy(minuscur.YminusX, cur.YplusX);
fe_copy(minuscur.Z, cur.Z);
fe_neg(minuscur.T2d, cur.T2d);
ge_cached_cmov(&cur, &minuscur, bnegative);
ge_add(&t, &u, &cur);
if (i == 0)
ge_p1p1_to_p3(r3, &t);
else
ge_p1p1_to_p2(&r, &t);
}
}
void ge_double_scalarmult_precomp_vartime2(ge_p2 *r, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi) {
signed char aslide[256];
signed char bslide[256];
ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */
ge_p1p1 t;
ge_p3 u;
int i;
slide(aslide, a);
slide(bslide, b);
ge_dsm_precomp(Ai, A);
ge_p2_0(r);
@ -2041,6 +2148,56 @@ void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, cons
}
}
void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *r3, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi) {
signed char aslide[256];
signed char bslide[256];
ge_p1p1 t;
ge_p3 u;
ge_p2 r;
int i;
slide(aslide, a);
slide(bslide, b);
ge_p2_0(&r);
for (i = 255; i >= 0; --i) {
if (aslide[i] || bslide[i]) break;
}
for (; i >= 0; --i) {
ge_p2_dbl(&t, &r);
if (aslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_add(&t, &u, &Ai[aslide[i]/2]);
} else if (aslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_sub(&t, &u, &Ai[(-aslide[i])/2]);
}
if (bslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_add(&t, &u, &Bi[bslide[i]/2]);
} else if (bslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_sub(&t, &u, &Bi[(-bslide[i])/2]);
}
if (i == 0)
ge_p1p1_to_p3(r3, &t);
else
ge_p1p1_to_p2(&r, &t);
}
}
void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) {
ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */
ge_dsm_precomp(Ai, A);
ge_double_scalarmult_precomp_vartime2(r, a, Ai, b, Bi);
}
void ge_mul8(ge_p1p1 *r, const ge_p2 *t) {
ge_p2 u;
ge_p2_dbl(r, t);
@ -2898,6 +3055,658 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b,
s[31] = s11 >> 17;
}
//copied from above and modified
/*
Input:
a[0]+256*a[1]+...+256^31*a[31] = a
b[0]+256*b[1]+...+256^31*b[31] = b
Output:
s[0]+256*s[1]+...+256^31*s[31] = (ab) mod l
where l = 2^252 + 27742317777372353535851937790883648493.
*/
void sc_mul(unsigned char *s, const unsigned char *a, const unsigned char *b) {
int64_t a0 = 2097151 & load_3(a);
int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
int64_t a8 = 2097151 & load_3(a + 21);
int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
int64_t a11 = (load_4(a + 28) >> 7);
int64_t b0 = 2097151 & load_3(b);
int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
int64_t b8 = 2097151 & load_3(b + 21);
int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
int64_t b11 = (load_4(b + 28) >> 7);
int64_t s0;
int64_t s1;
int64_t s2;
int64_t s3;
int64_t s4;
int64_t s5;
int64_t s6;
int64_t s7;
int64_t s8;
int64_t s9;
int64_t s10;
int64_t s11;
int64_t s12;
int64_t s13;
int64_t s14;
int64_t s15;
int64_t s16;
int64_t s17;
int64_t s18;
int64_t s19;
int64_t s20;
int64_t s21;
int64_t s22;
int64_t s23;
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
int64_t carry10;
int64_t carry11;
int64_t carry12;
int64_t carry13;
int64_t carry14;
int64_t carry15;
int64_t carry16;
int64_t carry17;
int64_t carry18;
int64_t carry19;
int64_t carry20;
int64_t carry21;
int64_t carry22;
s0 = a0*b0;
s1 = (a0*b1 + a1*b0);
s2 = (a0*b2 + a1*b1 + a2*b0);
s3 = (a0*b3 + a1*b2 + a2*b1 + a3*b0);
s4 = (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0);
s5 = (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0);
s6 = (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0);
s7 = (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0);
s8 = (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0);
s9 = (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0);
s10 = (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0);
s11 = (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0);
s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1);
s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2);
s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3);
s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4);
s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5);
s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6);
s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7);
s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8);
s20 = (a9*b11 + a10*b10 + a11*b9);
s21 = (a10*b11 + a11*b10);
s22 = a11*b11;
s23 = 0;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21;
carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21;
carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21;
carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21;
carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
s[0] = s0 >> 0;
s[1] = s0 >> 8;
s[2] = (s0 >> 16) | (s1 << 5);
s[3] = s1 >> 3;
s[4] = s1 >> 11;
s[5] = (s1 >> 19) | (s2 << 2);
s[6] = s2 >> 6;
s[7] = (s2 >> 14) | (s3 << 7);
s[8] = s3 >> 1;
s[9] = s3 >> 9;
s[10] = (s3 >> 17) | (s4 << 4);
s[11] = s4 >> 4;
s[12] = s4 >> 12;
s[13] = (s4 >> 20) | (s5 << 1);
s[14] = s5 >> 7;
s[15] = (s5 >> 15) | (s6 << 6);
s[16] = s6 >> 2;
s[17] = s6 >> 10;
s[18] = (s6 >> 18) | (s7 << 3);
s[19] = s7 >> 5;
s[20] = s7 >> 13;
s[21] = s8 >> 0;
s[22] = s8 >> 8;
s[23] = (s8 >> 16) | (s9 << 5);
s[24] = s9 >> 3;
s[25] = s9 >> 11;
s[26] = (s9 >> 19) | (s10 << 2);
s[27] = s10 >> 6;
s[28] = (s10 >> 14) | (s11 << 7);
s[29] = s11 >> 1;
s[30] = s11 >> 9;
s[31] = s11 >> 17;
}
//copied from above and modified
/*
Input:
a[0]+256*a[1]+...+256^31*a[31] = a
b[0]+256*b[1]+...+256^31*b[31] = b
c[0]+256*c[1]+...+256^31*c[31] = c
Output:
s[0]+256*s[1]+...+256^31*s[31] = (c+ab) mod l
where l = 2^252 + 27742317777372353535851937790883648493.
*/
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) {
int64_t a0 = 2097151 & load_3(a);
int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
int64_t a8 = 2097151 & load_3(a + 21);
int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
int64_t a11 = (load_4(a + 28) >> 7);
int64_t b0 = 2097151 & load_3(b);
int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
int64_t b8 = 2097151 & load_3(b + 21);
int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
int64_t b11 = (load_4(b + 28) >> 7);
int64_t c0 = 2097151 & load_3(c);
int64_t c1 = 2097151 & (load_4(c + 2) >> 5);
int64_t c2 = 2097151 & (load_3(c + 5) >> 2);
int64_t c3 = 2097151 & (load_4(c + 7) >> 7);
int64_t c4 = 2097151 & (load_4(c + 10) >> 4);
int64_t c5 = 2097151 & (load_3(c + 13) >> 1);
int64_t c6 = 2097151 & (load_4(c + 15) >> 6);
int64_t c7 = 2097151 & (load_3(c + 18) >> 3);
int64_t c8 = 2097151 & load_3(c + 21);
int64_t c9 = 2097151 & (load_4(c + 23) >> 5);
int64_t c10 = 2097151 & (load_3(c + 26) >> 2);
int64_t c11 = (load_4(c + 28) >> 7);
int64_t s0;
int64_t s1;
int64_t s2;
int64_t s3;
int64_t s4;
int64_t s5;
int64_t s6;
int64_t s7;
int64_t s8;
int64_t s9;
int64_t s10;
int64_t s11;
int64_t s12;
int64_t s13;
int64_t s14;
int64_t s15;
int64_t s16;
int64_t s17;
int64_t s18;
int64_t s19;
int64_t s20;
int64_t s21;
int64_t s22;
int64_t s23;
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
int64_t carry10;
int64_t carry11;
int64_t carry12;
int64_t carry13;
int64_t carry14;
int64_t carry15;
int64_t carry16;
int64_t carry17;
int64_t carry18;
int64_t carry19;
int64_t carry20;
int64_t carry21;
int64_t carry22;
s0 = c0 + a0*b0;
s1 = c1 + (a0*b1 + a1*b0);
s2 = c2 + (a0*b2 + a1*b1 + a2*b0);
s3 = c3 + (a0*b3 + a1*b2 + a2*b1 + a3*b0);
s4 = c4 + (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0);
s5 = c5 + (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0);
s6 = c6 + (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0);
s7 = c7 + (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0);
s8 = c8 + (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0);
s9 = c9 + (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0);
s10 = c10 + (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0);
s11 = c11 + (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0);
s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1);
s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2);
s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3);
s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4);
s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5);
s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6);
s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7);
s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8);
s20 = (a9*b11 + a10*b10 + a11*b9);
s21 = (a10*b11 + a11*b10);
s22 = a11*b11;
s23 = 0;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21;
carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21;
carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21;
carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21;
carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
s[0] = s0 >> 0;
s[1] = s0 >> 8;
s[2] = (s0 >> 16) | (s1 << 5);
s[3] = s1 >> 3;
s[4] = s1 >> 11;
s[5] = (s1 >> 19) | (s2 << 2);
s[6] = s2 >> 6;
s[7] = (s2 >> 14) | (s3 << 7);
s[8] = s3 >> 1;
s[9] = s3 >> 9;
s[10] = (s3 >> 17) | (s4 << 4);
s[11] = s4 >> 4;
s[12] = s4 >> 12;
s[13] = (s4 >> 20) | (s5 << 1);
s[14] = s5 >> 7;
s[15] = (s5 >> 15) | (s6 << 6);
s[16] = s6 >> 2;
s[17] = s6 >> 10;
s[18] = (s6 >> 18) | (s7 << 3);
s[19] = s7 >> 5;
s[20] = s7 >> 13;
s[21] = s8 >> 0;
s[22] = s8 >> 8;
s[23] = (s8 >> 16) | (s9 << 5);
s[24] = s9 >> 3;
s[25] = s9 >> 11;
s[26] = (s9 >> 19) | (s10 << 2);
s[27] = s10 >> 6;
s[28] = (s10 >> 14) | (s11 << 7);
s[29] = s11 >> 1;
s[30] = s11 >> 9;
s[31] = s11 >> 17;
}
/* Assumes that a != INT64_MIN */
static int64_t signum(int64_t a) {
return (a >> 63) - ((-a) >> 63);

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -79,6 +79,7 @@ typedef ge_cached ge_dsmp[8];
extern const ge_precomp ge_Bi[8];
void ge_dsm_precomp(ge_dsmp r, const ge_p3 *s);
void ge_double_scalarmult_base_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *);
void ge_double_scalarmult_base_vartime_p3(ge_p3 *, const unsigned char *, const ge_p3 *, const unsigned char *);
/* From ge_frombytes.c, modified */
@ -127,7 +128,10 @@ void sc_reduce(unsigned char *);
/* New code */
void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *);
void ge_scalarmult_p3(ge_p3 *, const unsigned char *, const ge_p3 *);
void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp);
void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_mul8(ge_p1p1 *, const ge_p2 *);
extern const fe fe_ma2;
extern const fe fe_ma;
@ -135,12 +139,15 @@ extern const fe fe_fffb1;
extern const fe fe_fffb2;
extern const fe fe_fffb3;
extern const fe fe_fffb4;
extern const ge_p3 ge_p3_identity;
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
void sc_0(unsigned char *);
void sc_reduce32(unsigned char *);
void sc_add(unsigned char *, const unsigned char *, const unsigned char *);
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *);
void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *);
void sc_mul(unsigned char *, const unsigned char *, const unsigned char *);
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c);
int sc_check(const unsigned char *);
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -43,6 +43,18 @@
#include "crypto.h"
#include "hash.h"
namespace {
static void local_abort(const char *msg)
{
fprintf(stderr, "%s\n", msg);
#ifdef NDEBUG
_exit(1);
#else
abort();
#endif
}
}
namespace crypto {
using std::abort;
@ -87,14 +99,14 @@ namespace crypto {
random_scalar_not_thread_safe(res);
}
static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
cn_fast_hash(data, length, reinterpret_cast<hash &>(res));
sc_reduce32(&res);
}
/*
* generate public and secret keys from a random 256-bit integer
* TODO: allow specifiying random value (for wallet recovery)
* TODO: allow specifying random value (for wallet recovery)
*
*/
secret_key crypto_ops::generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover) {
@ -189,6 +201,25 @@ namespace crypto {
sc_add(&derived_key, &base, &scalar);
}
bool crypto_ops::derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &derived_key) {
ec_scalar scalar;
ge_p3 point1;
ge_p3 point2;
ge_cached point3;
ge_p1p1 point4;
ge_p2 point5;
if (ge_frombytes_vartime(&point1, &out_key) != 0) {
return false;
}
derivation_to_scalar(derivation, output_index, scalar);
ge_scalarmult_base(&point2, &scalar);
ge_p3_to_cached(&point3, &point2);
ge_sub(&point4, &point1, &point3);
ge_p1p1_to_p2(&point5, &point4);
ge_tobytes(&derived_key, &point5);
return true;
}
struct s_comm {
hash h;
ec_point key;
@ -246,22 +277,33 @@ namespace crypto {
return sc_isnonzero(&c) == 0;
}
void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) {
void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
// sanity check
ge_p3 R_p3;
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 D_p3;
if (ge_frombytes_vartime(&R_p3, &R) != 0) throw std::runtime_error("tx pubkey is invalid");
if (ge_frombytes_vartime(&A_p3, &A) != 0) throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) throw std::runtime_error("recipient spend pubkey is invalid");
if (ge_frombytes_vartime(&D_p3, &D) != 0) throw std::runtime_error("key derivation is invalid");
#if !defined(NDEBUG)
{
assert(sc_check(&r) == 0);
// check R == r*G
ge_p3 dbg_R_p3;
ge_scalarmult_base(&dbg_R_p3, &r);
// check R == r*G or R == r*B
public_key dbg_R;
ge_p3_tobytes(&dbg_R, &dbg_R_p3);
if (B)
{
ge_p2 dbg_R_p2;
ge_scalarmult(&dbg_R_p2, &r, &B_p3);
ge_tobytes(&dbg_R, &dbg_R_p2);
}
else
{
ge_p3 dbg_R_p3;
ge_scalarmult_base(&dbg_R_p3, &r);
ge_p3_tobytes(&dbg_R, &dbg_R_p3);
}
assert(R == dbg_R);
// check D == r*A
ge_p2 dbg_D_p2;
@ -276,43 +318,84 @@ namespace crypto {
ec_scalar k;
random_scalar(k);
// compute X = k*G
ge_p3 X_p3;
ge_scalarmult_base(&X_p3, &k);
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D;
if (B)
{
// compute X = k*B
ge_p2 X_p2;
ge_scalarmult(&X_p2, &k, &B_p3);
ge_tobytes(&buf.X, &X_p2);
}
else
{
// compute X = k*G
ge_p3 X_p3;
ge_scalarmult_base(&X_p3, &k);
ge_p3_tobytes(&buf.X, &X_p3);
}
// compute Y = k*A
ge_p2 Y_p2;
ge_scalarmult(&Y_p2, &k, &A_p3);
ge_tobytes(&buf.Y, &Y_p2);
// sig.c = Hs(Msg || D || X || Y)
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D;
ge_p3_tobytes(&buf.X, &X_p3);
ge_tobytes(&buf.Y, &Y_p2);
hash_to_scalar(&buf, sizeof(s_comm_2), sig.c);
hash_to_scalar(&buf, sizeof(buf), sig.c);
// sig.r = k - sig.c*r
sc_mulsub(&sig.r, &sig.c, &r, &k);
}
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) {
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
// sanity check
ge_p3 R_p3;
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 D_p3;
if (ge_frombytes_vartime(&R_p3, &R) != 0) return false;
if (ge_frombytes_vartime(&A_p3, &A) != 0) return false;
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) return false;
if (ge_frombytes_vartime(&D_p3, &D) != 0) return false;
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) return false;
// compute sig.c*R
ge_p2 cR_p2;
ge_scalarmult(&cR_p2, &sig.c, &R_p3);
ge_p3 cR_p3;
{
ge_p2 cR_p2;
ge_scalarmult(&cR_p2, &sig.c, &R_p3);
public_key cR;
ge_tobytes(&cR, &cR_p2);
if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false;
}
// compute sig.r*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_p1p1 X_p1p1;
if (B)
{
// compute X = sig.c*R + sig.r*B
ge_p2 rB_p2;
ge_scalarmult(&rB_p2, &sig.r, &B_p3);
public_key rB;
ge_tobytes(&rB, &rB_p2);
ge_p3 rB_p3;
if (ge_frombytes_vartime(&rB_p3, &rB) != 0) return false;
ge_cached rB_cached;
ge_p3_to_cached(&rB_cached, &rB_p3);
ge_add(&X_p1p1, &cR_p3, &rB_cached);
}
else
{
// compute X = sig.c*R + sig.r*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_add(&X_p1p1, &cR_p3, &rG_cached);
}
ge_p2 X_p2;
ge_p1p1_to_p2(&X_p2, &X_p1p1);
// compute sig.c*D
ge_p2 cD_p2;
@ -322,18 +405,6 @@ namespace crypto {
ge_p2 rA_p2;
ge_scalarmult(&rA_p2, &sig.r, &A_p3);
// compute X = sig.c*R + sig.r*G
public_key cR;
ge_tobytes(&cR, &cR_p2);
ge_p3 cR_p3;
if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false;
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_p1p1 X_p1p1;
ge_add(&X_p1p1, &cR_p3, &rG_cached);
ge_p2 X_p2;
ge_p1p1_to_p2(&X_p2, &X_p1p1);
// compute Y = sig.c*D + sig.r*A
public_key cD;
public_key rA;
@ -408,7 +479,7 @@ POP_WARNINGS
ec_scalar sum, k, h;
boost::shared_ptr<rs_comm> buf(reinterpret_cast<rs_comm *>(malloc(rs_comm_size(pubs_count))), free);
if (!buf)
abort();
local_abort("malloc failure");
assert(sec_index < pubs_count);
#if !defined(NDEBUG)
{
@ -427,7 +498,7 @@ POP_WARNINGS
}
#endif
if (ge_frombytes_vartime(&image_unp, &image) != 0) {
abort();
local_abort("invalid key image");
}
ge_dsm_precomp(image_pre, &image_unp);
sc_0(&sum);
@ -446,7 +517,7 @@ POP_WARNINGS
random_scalar(sig[i].c);
random_scalar(sig[i].r);
if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) {
abort();
local_abort("invalid pubkey");
}
ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r);
ge_tobytes(&buf->ab[i].a, &tmp2);

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -31,12 +31,20 @@
#pragma once
#include <cstddef>
#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/optional.hpp>
#include <type_traits>
#include <vector>
#include "common/pod-class.h"
#include "common/util.h"
#include "memwipe.h"
#include "generic-ops.h"
#include "hex.h"
#include "span.h"
#include "hash.h"
namespace crypto {
@ -60,9 +68,7 @@ namespace crypto {
friend class crypto_ops;
};
POD_CLASS secret_key: ec_scalar {
friend class crypto_ops;
};
using secret_key = tools::scrubbed<ec_scalar>;
POD_CLASS public_keyV {
std::vector<public_key> keys;
@ -94,6 +100,8 @@ namespace crypto {
};
#pragma pack(pop)
void hash_to_scalar(const void *data, size_t length, ec_scalar &res);
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(secret_key) == 32 &&
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 &&
@ -119,14 +127,16 @@ namespace crypto {
friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &);
static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
static bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &);
friend bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &);
static void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
static bool check_signature(const hash &, const public_key &, const signature &);
friend bool check_signature(const hash &, const public_key &, const signature &);
static void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &);
static void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
static void generate_key_image(const public_key &, const secret_key &, key_image &);
friend void generate_key_image(const public_key &, const secret_key &, key_image &);
static void generate_ring_signature(const hash &, const key_image &,
@ -194,6 +204,9 @@ namespace crypto {
const secret_key &base, secret_key &derived_key) {
crypto_ops::derive_secret_key(derivation, output_index, base, derived_key);
}
inline bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result) {
return crypto_ops::derive_subaddress_public_key(out_key, derivation, output_index, result);
}
/* Generation and checking of a standard signature.
*/
@ -206,12 +219,13 @@ namespace crypto {
/* Generation and checking of a tx proof; given a tx pubkey R, the recipient's view pubkey A, and the key
* derivation D, the signature proves the knowledge of the tx secret key r such that R=r*G and D=r*A
* When the recipient's address is a subaddress, the tx pubkey R is defined as R=r*B where B is the recipient's spend pubkey
*/
inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_tx_proof(prefix_hash, R, A, D, r, sig);
inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, D, sig);
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig);
}
/* To send money to a key:
@ -248,8 +262,28 @@ namespace crypto {
const signature *sig) {
return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig);
}
inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
const static crypto::public_key null_pkey = boost::value_initialized<crypto::public_key>();
const static crypto::secret_key null_skey = boost::value_initialized<crypto::secret_key>();
}
CRYPTO_MAKE_HASHABLE(public_key)
CRYPTO_MAKE_HASHABLE(secret_key)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_COMPARABLE(signature)

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//

Some files were not shown because too many files have changed in this diff Show More