Skip to content

Commit

Permalink
Merge pull request #98 from qubic/develop
Browse files Browse the repository at this point in the history
Release/v1.202 to main
  • Loading branch information
philippwerner authored May 7, 2024
2 parents 38d76a2 + 547c9ce commit 63ddb33
Show file tree
Hide file tree
Showing 25 changed files with 1,605 additions and 376 deletions.
3 changes: 3 additions & 0 deletions src/Qubic.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<ClCompile Include="qubic.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="addons\tx_status_request.h" />
<ClInclude Include="assets.h" />
<ClInclude Include="contracts\math_lib.h" />
<ClInclude Include="contracts\qpi.h" />
Expand All @@ -26,6 +27,7 @@
<ClInclude Include="contracts\Qx.h" />
<ClInclude Include="contracts\Random.h" />
<ClInclude Include="contract_core\contract_def.h" />
<ClInclude Include="contract_core\contract_exec.h" />
<ClInclude Include="contract_core\stack_buffer.h" />
<ClInclude Include="logging.h" />
<ClInclude Include="network_core\peers.h" />
Expand All @@ -52,6 +54,7 @@
<ClInclude Include="platform\console_logging.h" />
<ClInclude Include="platform\common_types.h" />
<ClInclude Include="platform\random.h" />
<ClInclude Include="platform\read_write_lock.h" />
<ClInclude Include="platform\time_stamp_counter.h" />
<ClInclude Include="score.h" />
<ClInclude Include="platform\m256.h" />
Expand Down
14 changes: 13 additions & 1 deletion src/Qubic.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@
<ClInclude Include="contract_core\stack_buffer.h">
<Filter>contract_core</Filter>
</ClInclude>
<ClInclude Include="addons\tx_status_request.h">
<Filter>addons</Filter>
</ClInclude>
<ClInclude Include="contract_core\contract_exec.h">
<Filter>contract_core</Filter>
</ClInclude>
<ClInclude Include="platform\read_write_lock.h">
<Filter>platform</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="platform">
Expand All @@ -137,5 +146,8 @@
<Filter Include="contract_core">
<UniqueIdentifier>{e18dc5b0-6ba9-4b8a-9221-71165f0335b9}</UniqueIdentifier>
</Filter>
<Filter Include="addons">
<UniqueIdentifier>{d409258b-a63c-443f-8c7a-918c14f192a6}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>
246 changes: 246 additions & 0 deletions src/addons/tx_status_request.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// This is an extension by qli allowing to query transaction status with RequestTxStatus message

#pragma once

#if ADDON_TX_STATUS_REQUEST

#include "../platform/m256.h"
#include "../platform/debugging.h"
#include "../platform/memory.h"

#include "../network_messages/header.h"

#include "../public_settings.h"
#include "../system.h"


typedef struct
{
unsigned int tick;
unsigned char moneyFlew;
unsigned char _padding[3];
m256i digest;
} ConfirmedTx;

constexpr unsigned long long confirmedTxCurrentEpochLength = (((unsigned long long)MAX_NUMBER_OF_TICKS_PER_EPOCH) * NUMBER_OF_TRANSACTIONS_PER_TICK / TRANSACTION_SPARSENESS);
constexpr unsigned long long confirmedTxPreviousEpochLength = (((unsigned long long)TICKS_TO_KEEP_FROM_PRIOR_EPOCH) * NUMBER_OF_TRANSACTIONS_PER_TICK / TRANSACTION_SPARSENESS);
constexpr unsigned long long confirmedTxLength = confirmedTxCurrentEpochLength + confirmedTxPreviousEpochLength;

// Memory to store confirmed TX's (arrays store data of current epoch, followed by last ticks of previous epoch)
static ConfirmedTx *confirmedTx = NULL;
static unsigned int tickTxCounter[MAX_NUMBER_OF_TICKS_PER_EPOCH + TICKS_TO_KEEP_FROM_PRIOR_EPOCH]; // store the amount of tx per tick
static unsigned int tickTxIndexStart[MAX_NUMBER_OF_TICKS_PER_EPOCH + TICKS_TO_KEEP_FROM_PRIOR_EPOCH]; // store the index position per tick
static volatile char confirmedTxLock = 0;
static unsigned int confirmedTxPreviousEpochBeginTick = 0; // first tick kept from previous epoch (or 0 if no tick from prev. epoch available)
static unsigned int confirmedTxCurrentEpochBeginTick = 0; // first tick of current epoch stored

#define REQUEST_TX_STATUS 201

struct RequestTxStatus
{
unsigned int tick;
};

static_assert(sizeof(RequestTxStatus) == 4, "unexpected size");

#define RESPOND_TX_STATUS 202

#pragma pack(push, 1)
struct RespondTxStatus
{
unsigned int currentTickOfNode;
unsigned int tick;
unsigned int txCount;
unsigned char moneyFlew[(NUMBER_OF_TRANSACTIONS_PER_TICK + 7) / 8];

// only txCount digests are sent with this message, so only read the first txCount digests when using this as a view to the received data
m256i txDigests[NUMBER_OF_TRANSACTIONS_PER_TICK];

// return size of this struct to be sent (last txDigests are 0 and do not need to be sent)
unsigned int size() const
{
return offsetof(RespondTxStatus, txDigests) + txCount * sizeof(m256i);
}
};
#pragma pack(pop)
static RespondTxStatus* tickTxStatusStorage = NULL;

// Allocate buffers
static bool initTxStatusRequestAddOn()
{
// Allocate pool to store confirmed TX's
if (!allocatePool(confirmedTxLength * sizeof(ConfirmedTx), (void**)&confirmedTx))
return false;
// allocate tickTxStatus responses storage
if (!allocatePool(MAX_NUMBER_OF_PROCESSORS * sizeof(RespondTxStatus), (void**)&tickTxStatusStorage))
return false;
confirmedTxPreviousEpochBeginTick = 0;
confirmedTxCurrentEpochBeginTick = 0;
return true;
}


// Free buffers
static void deinitTxStatusRequestAddOn()
{
if (confirmedTx)
freePool(confirmedTx);
}


// Begin new epoch. If not called the first time (seamless transition), assume that the ticks to keep
// are ticks in [newInitialTick-TICKS_TO_KEEP_FROM_PRIOR_EPOCH, newInitialTick-1].
static void beginEpochTxStatusRequestAddOn(unsigned int newInitialTick)
{
unsigned int& tickBegin = confirmedTxCurrentEpochBeginTick;
unsigned int& oldTickBegin = confirmedTxPreviousEpochBeginTick;

bool keepTicks = tickBegin && newInitialTick > tickBegin && newInitialTick <= tickBegin + MAX_NUMBER_OF_TICKS_PER_EPOCH;
if (keepTicks)
{
// Seamless epoch transition: keep some ticks of prior epoch
// The number of ticks to keep is limited by:
// - the length of the previous epoch tick buffer (tickCount < TICKS_TO_KEEP_FROM_PRIOR_EPOCH)
// - the length of the previous epoch confirmedTx array (confirmedTxPreviousEpochLength)
// - the number of ticks available from in ended epoch (tickIndex >= 0)
unsigned int tickCount = 0;
unsigned int txCount = 0;
for (int tickIndex = (newInitialTick - 1) - tickBegin; tickIndex >= 0; --tickIndex)
{
unsigned int newTxCount = txCount + tickTxCounter[tickIndex];
if (newTxCount <= confirmedTxPreviousEpochLength && tickCount < TICKS_TO_KEEP_FROM_PRIOR_EPOCH)
{
txCount = newTxCount;
++tickCount;
}
else
{
break;
}
}

oldTickBegin = newInitialTick - tickCount;
const unsigned int oldTickEnd = newInitialTick;
const unsigned int tickIndex = oldTickBegin - tickBegin;

// copy confirmedTx and tickTxCounter from recently ended epoch into storage of previous epoch
copyMem(tickTxCounter + MAX_NUMBER_OF_TICKS_PER_EPOCH, tickTxCounter + tickIndex, tickCount * sizeof(tickTxCounter[0]));
copyMem(confirmedTx + confirmedTxCurrentEpochLength, confirmedTx + tickTxIndexStart[tickIndex], txCount * sizeof(confirmedTx[0]));

// copy adjusted tickTxIndexStart
const unsigned int indexStartDelta = confirmedTxCurrentEpochLength - tickTxIndexStart[tickIndex];
for (unsigned int tickOffset = 0; tickOffset < tickCount; ++tickOffset)
{
tickTxIndexStart[MAX_NUMBER_OF_TICKS_PER_EPOCH + tickOffset] = tickTxIndexStart[tickIndex + tickOffset] + indexStartDelta;
}

// init pool of current epoch with 0
setMem(confirmedTx, confirmedTxCurrentEpochLength * sizeof(ConfirmedTx), 0);
setMem(tickTxCounter, MAX_NUMBER_OF_TICKS_PER_EPOCH * sizeof(tickTxCounter[0]), 0);
setMem(tickTxIndexStart, MAX_NUMBER_OF_TICKS_PER_EPOCH * sizeof(tickTxIndexStart[0]), 0);
}
else
{
// node startup with no data of prior epoch or no ticks to keep
oldTickBegin = 0;

// init pool with 0
setMem(confirmedTx, confirmedTxLength * sizeof(ConfirmedTx), 0);
setMem(tickTxCounter, sizeof(tickTxCounter), 0);
setMem(tickTxIndexStart, sizeof(tickTxIndexStart), 0);
}

tickBegin = newInitialTick;
}


// adds a tx to the confirmed tx store
// txNumberMinusOne: the current tx number -1
// moneyFlew: if money has been flow
// tick: tick of tx (needs to be in range of current epoch)
// digest: digest of tx
static bool saveConfirmedTx(unsigned int txNumberMinusOne, unsigned char moneyFlew, unsigned int tick, const m256i& digest)
{
ASSERT(confirmedTxCurrentEpochBeginTick == system.initialTick);
ASSERT(tick >= system.initialTick && tick < system.initialTick + MAX_NUMBER_OF_TICKS_PER_EPOCH);

// skip if confirmedTx storage is full
if (txNumberMinusOne >= confirmedTxCurrentEpochLength)
return false;

ACQUIRE(confirmedTxLock);

// set confirmedTx data
ConfirmedTx & txConfirmation = confirmedTx[txNumberMinusOne];
txConfirmation.tick = tick;
txConfirmation.moneyFlew = moneyFlew;
txConfirmation.digest = digest;

// get current tick number in epoch
int tickIndex = tick - system.initialTick;
// keep track of tx number in tick to find it later easier
tickTxCounter[tickIndex]++;

RELEASE(confirmedTxLock);

return true;
}


static void processRequestConfirmedTx(long long processorNumber, Peer *peer, RequestResponseHeader *header)
{
ASSERT(confirmedTxCurrentEpochBeginTick == system.initialTick);
ASSERT(system.tick >= system.initialTick);

RequestTxStatus *request = header->getPayload<RequestTxStatus>();

// only send a response if the node is in higher tick than the requested tx
if (request->tick >= system.tick)
return;

int tickIndex;
if (request->tick >= confirmedTxCurrentEpochBeginTick && request->tick < confirmedTxCurrentEpochBeginTick + MAX_NUMBER_OF_TICKS_PER_EPOCH)
{
// current epoch
tickIndex = request->tick - confirmedTxCurrentEpochBeginTick;
}
else if (confirmedTxPreviousEpochBeginTick != 0 && request->tick >= confirmedTxPreviousEpochBeginTick && request->tick < confirmedTxCurrentEpochBeginTick)
{
// available tick of previous epoch (stored behind current epoch data)
tickIndex = request->tick - confirmedTxPreviousEpochBeginTick + MAX_NUMBER_OF_TICKS_PER_EPOCH;
}
else
{
// tick not available
return;
}

// get index where confirmedTx are starting to be stored in memory
int index = tickTxIndexStart[tickIndex];

// init response message data, get it from the storage to avoid increasing stack mem
RespondTxStatus& tickTxStatus = tickTxStatusStorage[processorNumber];
tickTxStatus.currentTickOfNode = system.tick;
tickTxStatus.tick = request->tick;
tickTxStatus.txCount = tickTxCounter[tickIndex];
setMem(&tickTxStatus.moneyFlew, sizeof(tickTxStatus.moneyFlew), 0);

// loop over the number of tx which were stored for the given tick
ASSERT(tickTxStatus.txCount <= NUMBER_OF_TRANSACTIONS_PER_TICK);
for (unsigned int i = 0; i < tickTxStatus.txCount; i++)
{
ConfirmedTx& localConfirmedTx = confirmedTx[index + i];

ASSERT(index + i < confirmedTxLength);
ASSERT(localConfirmedTx.tick == request->tick);
ASSERT(localConfirmedTx.moneyFlew == 1 || localConfirmedTx.moneyFlew == 0);

tickTxStatus.txDigests[i] = localConfirmedTx.digest;
tickTxStatus.moneyFlew[i >> 3] |= (localConfirmedTx.moneyFlew << (i & 7));
}

ASSERT(tickTxStatus.size() <= sizeof(tickTxStatus));
enqueueResponse(peer, tickTxStatus.size(), RESPOND_TX_STATUS, header->dejavu(), &tickTxStatus);
}

#endif
Loading

0 comments on commit 63ddb33

Please sign in to comment.