Skip to content

Commit

Permalink
Add RandomSCASH Algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaolin1579 committed Aug 28, 2024
1 parent eaf0b80 commit f15f5bc
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Miningcore/Blockchain/Cryptonote/CryptonoteJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public CryptonoteJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId,
{
{ CryptonightHashType.RandomX, (realm, seedHex, data, result, _) => RandomX.CalculateHash(realm, seedHex, data, result) },
{ CryptonightHashType.RandomARQ, (realm, seedHex, data, result, _) => RandomARQ.CalculateHash(realm, seedHex, data, result) },
{ CryptonightHashType.RandomSCASH, (realm, seedHex, data, result, _) => RandomSCASH.CalculateHash(realm, seedHex, data, result) },
{ CryptonightHashType.Cryptonight0, (_, _, data, result, height) => Cryptonight.CryptonightHash(data, result, CN_0, height) },
{ CryptonightHashType.Cryptonight1, (_, _, data, result, height) => Cryptonight.CryptonightHash(data, result, CN_1, height) },
{ CryptonightHashType.Cryptonight2, (_, _, data, result, height) => Cryptonight.CryptonightHash(data, result, CN_2, height) },
Expand Down
28 changes: 28 additions & 0 deletions src/Miningcore/Blockchain/Cryptonote/CryptonoteJobManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,34 @@ private void UpdateHashParams(GetBlockTemplateResponse blockTemplate)

break;
}

case CryptonightHashType.RandomSCASH:
{
// detect seed hash change
if(currentSeedHash != blockTemplate.SeedHash)
{
logger.Info(()=> $"Detected new seed hash {blockTemplate.SeedHash} starting @ height {blockTemplate.Height}");

if(poolConfig.EnableInternalStratum == true)
{
RandomSCASH.WithLock(() =>
{
// delete old seed
if(currentSeedHash != null)
RandomSCASH.DeleteSeed(randomXRealm, currentSeedHash);

// activate new one
currentSeedHash = blockTemplate.SeedHash;
RandomSCASH.CreateSeed(randomXRealm, currentSeedHash, randomXFlagsOverride, randomXFlagsAdd, extraPoolConfig.RandomXVMCount);
});
}

else
currentSeedHash = blockTemplate.SeedHash;
}

break;
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/Miningcore/Configuration/ClusterConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,9 @@ public enum CryptonightHashType
[EnumMember(Value = "randomarq")]
RandomARQ,

[EnumMember(Value = "randomscash")]
RandomSCASH,

[EnumMember(Value = "cn0")]
Cryptonight0,

Expand Down
312 changes: 312 additions & 0 deletions src/Miningcore/Native/RandomSCASH.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Miningcore.Contracts;
using Miningcore.Extensions;
using Miningcore.Messaging;
using Miningcore.Notifications.Messages;
using NLog;

// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming

namespace Miningcore.Native;

public static unsafe class RandomSCASH
{
private static readonly ILogger logger = LogManager.GetCurrentClassLogger();
internal static IMessageBus messageBus;

#region VM managment

internal static readonly Dictionary<string, Dictionary<string, Tuple<GenContext, BlockingCollection<RxVm>>>> realms = new();
private static readonly byte[] empty = new byte[32];

#endregion // VM managment

[DllImport("librandomscash", EntryPoint = "randomx_get_flags", CallingConvention = CallingConvention.Cdecl)]
private static extern RandomX.randomx_flags get_flags();

[DllImport("librandomscash", EntryPoint = "randomx_alloc_cache", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr alloc_cache(RandomX.randomx_flags flags);

[DllImport("librandomscash", EntryPoint = "randomx_init_cache", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr init_cache(IntPtr cache, IntPtr key, int keysize);

[DllImport("librandomscash", EntryPoint = "randomx_release_cache", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr release_cache(IntPtr cache);

[DllImport("librandomscash", EntryPoint = "randomx_alloc_dataset", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr alloc_dataset(RandomX.randomx_flags flags);

[DllImport("librandomscash", EntryPoint = "randomx_dataset_item_count", CallingConvention = CallingConvention.Cdecl)]
private static extern ulong dataset_item_count();

[DllImport("librandomscash", EntryPoint = "randomx_init_dataset", CallingConvention = CallingConvention.Cdecl)]
private static extern void init_dataset(IntPtr dataset, IntPtr cache, ulong startItem, ulong itemCount);

[DllImport("librandomscash", EntryPoint = "randomx_get_dataset_memory", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_dataset_memory(IntPtr dataset);

[DllImport("librandomscash", EntryPoint = "randomx_release_dataset", CallingConvention = CallingConvention.Cdecl)]
private static extern void release_dataset(IntPtr dataset);

[DllImport("librandomscash", EntryPoint = "randomx_create_vm", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr create_vm(RandomX.randomx_flags flags, IntPtr cache, IntPtr dataset);

[DllImport("librandomscash", EntryPoint = "randomx_vm_set_cache", CallingConvention = CallingConvention.Cdecl)]
private static extern void vm_set_cache(IntPtr machine, IntPtr cache);

[DllImport("librandomscash", EntryPoint = "randomx_vm_set_dataset", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr vm_set_dataset(IntPtr machine, IntPtr dataset);

[DllImport("librandomscash", EntryPoint = "randomx_destroy_vm", CallingConvention = CallingConvention.Cdecl)]
private static extern void destroy_vm(IntPtr machine);

[DllImport("librandomscash", EntryPoint = "randomx_calculate_hash", CallingConvention = CallingConvention.Cdecl)]
private static extern void calculate_hash(IntPtr machine, byte* input, int inputSize, byte* output);

public class GenContext
{
public DateTime LastAccess { get; set; } = DateTime.Now;
public int VmCount { get; init; }
}

public class RxDataSet : IDisposable
{
private IntPtr dataset = IntPtr.Zero;

public void Dispose()
{
if(dataset != IntPtr.Zero)
{
release_dataset(dataset);
dataset = IntPtr.Zero;
}
}

public IntPtr Init(RandomX.randomx_flags flags, IntPtr cache)
{
dataset = alloc_dataset(flags);

var itemCount = dataset_item_count();
init_dataset(dataset, cache, 0, itemCount);

return dataset;
}
}

public class RxVm : IDisposable
{
private IntPtr cache = IntPtr.Zero;
private IntPtr vm = IntPtr.Zero;
private RxDataSet ds;

public void Dispose()
{
if(vm != IntPtr.Zero)
{
destroy_vm(vm);
vm = IntPtr.Zero;
}

ds?.Dispose();

if(cache != IntPtr.Zero)
{
release_cache(cache);
cache = IntPtr.Zero;
}
}

public void Init(ReadOnlySpan<byte> key, RandomX.randomx_flags flags)
{
var ds_ptr = IntPtr.Zero;

// alloc cache
cache = alloc_cache(flags);

// init cache
fixed(byte* key_ptr = key)
{
init_cache(cache, (IntPtr) key_ptr, key.Length);
}

// Enable fast-mode? (requires 2GB+ memory per VM)
if((flags & RandomX.randomx_flags.RANDOMX_FLAG_FULL_MEM) != 0)
{
ds = new RxDataSet();
ds_ptr = ds.Init(flags, cache);

// cache is no longer needed in fast-mode
release_cache(cache);
cache = IntPtr.Zero;
}

vm = create_vm(flags, cache, ds_ptr);
}

public void CalculateHash(ReadOnlySpan<byte> data, Span<byte> result)
{
fixed (byte* input = data)
{
fixed (byte* output = result)
{
calculate_hash(vm, input, data.Length, output);
}
}
}
}

public static void WithLock(Action action)
{
lock(realms)
{
action();
}
}

public static void CreateSeed(string realm, string seedHex,
RandomX.randomx_flags? flagsOverride = null, RandomX.randomx_flags? flagsAdd = null, int vmCount = 1)
{
lock(realms)
{
if(!realms.TryGetValue(realm, out var seeds))
{
seeds = new Dictionary<string, Tuple<GenContext, BlockingCollection<RxVm>>>();

realms[realm] = seeds;
}

if(!seeds.TryGetValue(seedHex, out var seed))
{
var flags = flagsOverride ?? get_flags();

if(flagsAdd.HasValue)
flags |= flagsAdd.Value;

if (vmCount == -1)
vmCount = Environment.ProcessorCount;

seed = CreateSeed(realm, seedHex, flags, vmCount);

seeds[seedHex] = seed;
}
}
}

private static Tuple<GenContext, BlockingCollection<RxVm>> CreateSeed(string realm, string seedHex, RandomX.randomx_flags flags, int vmCount)
{
var vms = new BlockingCollection<RxVm>();

var seed = new Tuple<GenContext, BlockingCollection<RxVm>>(new GenContext
{
VmCount = vmCount
}, vms);

void createVm(int index)
{
var start = DateTime.Now;
logger.Info(() => $"Creating VM {realm}@{index + 1} [{flags}], hash {seedHex} ...");

var vm = new RxVm();
vm.Init(seedHex.HexToByteArray(), flags);

vms.Add(vm);

logger.Info(() => $"Created VM {realm}@{index + 1} in {DateTime.Now - start}");
};

Parallel.For(0, vmCount, createVm);

return seed;
}

public static void DeleteSeed(string realm, string seedHex)
{
Tuple<GenContext, BlockingCollection<RxVm>> seed;

lock(realms)
{
if(!realms.TryGetValue(realm, out var seeds))
return;

if(!seeds.Remove(seedHex, out seed))
return;
}

// dispose all VMs
var (ctx, col) = seed;
var remaining = ctx.VmCount;

while (remaining > 0)
{
var vm = col.Take();

logger.Info($"Disposing VM {ctx.VmCount - remaining} for realm {realm} and key {seedHex}");
vm.Dispose();

remaining--;
}
}

public static Tuple<GenContext, BlockingCollection<RxVm>> GetSeed(string realm, string seedHex)
{
lock(realms)
{
if(!realms.TryGetValue(realm, out var seeds))
return null;

if(!seeds.TryGetValue(seedHex, out var seed))
return null;

return seed;
}
}

public static void CalculateHash(string realm, string seedHex, ReadOnlySpan<byte> data, Span<byte> result)
{
Contract.Requires<ArgumentException>(result.Length >= 32);

var sw = Stopwatch.StartNew();
var success = false;

var (ctx, seedVms) = GetSeed(realm, seedHex);

if(ctx != null)
{
RxVm vm = null;

try
{
// lease a VM
vm = seedVms.Take();

vm.CalculateHash(data, result);

ctx.LastAccess = DateTime.Now;
success = true;

messageBus?.SendTelemetry("RandomSCASH", TelemetryCategory.Hash, sw.Elapsed, true);
}

catch(Exception ex)
{
logger.Error(() => ex.Message);
}

finally
{
// return it
if(vm != null)
seedVms.Add(vm);
}
}

if(!success)
{
// clear result on failure
empty.CopyTo(result);
}
}
}
3 changes: 3 additions & 0 deletions src/Miningcore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,9 @@ private static async Task PreFlightChecks(IServiceProvider services)
// Configure RandomARQ
RandomARQ.messageBus = messageBus;

// Configure RandomSCASH
RandomSCASH.messageBus = messageBus;

// Configure NexaPow
Miningcore.Crypto.Hashing.Algorithms.NexaPow.messageBus = messageBus;

Expand Down
1 change: 1 addition & 0 deletions src/Miningcore/build-libs-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ export HAVE_FEATURE="$HAVE_AES $HAVE_SSE2 $HAVE_SSE3 $HAVE_SSSE3 $HAVE_PCLMUL $H
((cd /tmp && rm -rf secp256k1 && git clone https://github.com/bitcoin-ABC/secp256k1 && cd secp256k1 && git checkout 04fabb44590c10a19e35f044d11eb5058aac65b2 && mkdir build && cd build && cmake -GNinja .. -DCMAKE_C_FLAGS=-fPIC -DSECP256K1_ENABLE_MODULE_RECOVERY=OFF -DSECP256K1_ENABLE_COVERAGE=OFF -DSECP256K1_ENABLE_MODULE_SCHNORR=ON && ninja) && (cd ../Native/libnexapow && cp /tmp/secp256k1/build/libsecp256k1.a . && make clean && make) && mv ../Native/libnexapow/libnexapow.so "$OutDir")
((cd /tmp && rm -rf RandomX && git clone https://github.com/tevador/RandomX && cd RandomX && git checkout tags/v1.1.10 && mkdir build && cd build && cmake -DARCH=native .. && make) && (cd ../Native/librandomx && cp /tmp/RandomX/build/librandomx.a . && make clean && make) && mv ../Native/librandomx/librandomx.so "$OutDir")
((cd /tmp && rm -rf RandomARQ && git clone https://github.com/arqma/RandomARQ && cd RandomARQ && git checkout 14850620439045b319fa6398f5a164715c4a66ce && mkdir build && cd build && cmake -DARCH=native .. && make) && (cd ../Native/librandomarq && cp /tmp/RandomARQ/build/librandomx.a . && make clean && make) && mv ../Native/librandomarq/librandomarq.so "$OutDir")
((cd /tmp && rm -rf RandomSCASH && git clone https://github.com/scashnetwork/RandomX RandomSCASH && cd RandomSCASH && mkdir build && cd build && cmake -DARCH=native .. && make) && (cd ../Native/librandomscash && cp /tmp/RandomSCASH/build/librandomx.a . && make clean && make) && mv ../Native/librandomscash/librandomscash.so "$OutDir")
17 changes: 17 additions & 0 deletions src/Native/librandomscash/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Create shared library librandomx.so from static library librandomx.a using --whole-archive

CC = g++
LDFLAGS = -shared -pthread -L. -Wl,-whole-archive librandomx.a -Wl,-no-whole-archive
LDLIBS = -lstdc++ -lgcc -lc
TARGET = librandomscash.so

all: $(TARGET)

$(TARGET): $(OBJECTS)
$(CC) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)

.PHONY: clean

clean:
find . -name '*.o' -exec rm -r {} \;
rm -f librandomscash.so

0 comments on commit f15f5bc

Please sign in to comment.