Skip to content

Commit d1de23b

Browse files
committed
fix:migration
1 parent e46a7e7 commit d1de23b

3 files changed

Lines changed: 307 additions & 1 deletion

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 6910c9d9165801d8827d628cb72eb7ea9dd538c5

src/contracts/QRaffle.h

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ constexpr uint64 QRAFFLE_MAX_ASSET_TICKET_AMOUNT = 1000000000000ull;// 1
7070
constexpr uint32 QRAFFLE_ASSET_RAFFLE_BUNDLE_FLAT_SIZE = QRAFFLE_MAX_ASSET_RAFFLES_PER_EPOCH * QRAFFLE_MAX_ASSETS_PER_BUNDLE; // 256
7171
constexpr uint32 QRAFFLE_ASSET_RAFFLE_BUYERS_FLAT_SIZE = QRAFFLE_MAX_ASSET_RAFFLES_PER_EPOCH * QRAFFLE_MAX_ASSET_TICKET_BUYERS; // 65536
7272

73+
74+
constexpr uint32 QRAFFLE_MAX_SHAREHOLDERS_OLD = 1024;
75+
76+
7377
struct QRAFFLE2
7478
{
7579
};
@@ -304,6 +308,35 @@ struct QRAFFLE : public ContractBase
304308
uint64 entryAmount;
305309
};
306310

311+
struct OldStateData
312+
{
313+
HashMap <id, uint8, QRAFFLE_MAX_MEMBER> registers;
314+
Array <ProposalInfo, QRAFFLE_MAX_PROPOSAL_EPOCH> proposals;
315+
316+
HashMap <id, BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH>, QRAFFLE_MAX_MEMBER> voteParticipation;
317+
HashMap <id, BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH>, QRAFFLE_MAX_MEMBER> voteValues;
318+
Array <uint32, QRAFFLE_MAX_PROPOSAL_EPOCH> numberOfVotedInProposal;
319+
Array <id, QRAFFLE_MAX_MEMBER> quRaffleMembers;
320+
HashSet <id, QRAFFLE_MAX_MEMBER> quRaffleMemberSet;
321+
322+
Array <ActiveTokenRaffleInfo, QRAFFLE_MAX_PROPOSAL_EPOCH> activeTokenRaffle;
323+
HashMap <id, BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH>, QRAFFLE_MAX_MEMBER> tokenRaffleParticipation;
324+
Array <id, QRAFFLE_MAX_MEMBER> tokenRaffleMemberSlots;
325+
Array <uint32, QRAFFLE_MAX_PROPOSAL_EPOCH> numberOfTokenRaffleMembers;
326+
327+
Array <QuRaffleInfo, QRAFFLE_MAX_EPOCH> QuRaffles;
328+
Array <TokenRaffleInfo, QRAFFLE_MAX_TOKEN_RAFFLES> tokenRaffle;
329+
HashMap <id, uint64, QRAFFLE_MAX_MEMBER> quRaffleEntryAmount;
330+
HashSet <id, QRAFFLE_MAX_SHAREHOLDERS_OLD> shareholdersList;
331+
332+
id initialRegister1, initialRegister2, initialRegister3, initialRegister4, initialRegister5;
333+
id charityAddress, feeAddress, QXMRIssuer;
334+
uint64 epochRevenue, epochQXMRRevenue, qREAmount, totalBurnAmount, totalCharityAmount, totalShareholderAmount, totalRegisterAmount, totalFeeAmount, totalWinnerAmount, largestWinnerAmount;
335+
uint32 numberOfRegisters, numberOfQuRaffleMembers, numberOfEntryAmountSubmitted, numberOfProposals, numberOfActiveTokenRaffle, numberOfEndedTokenRaffle;
336+
Array<uint32, QRAFFLE_MAX_EPOCH> daoMemberCount;
337+
HashMap <id, uint8, QRAFFLE_MAX_MEMBER> proposalsPerProposer;
338+
};
339+
307340
struct StateData
308341
{
309342
HashMap <id, uint8, QRAFFLE_MAX_MEMBER> registers;
@@ -2738,6 +2771,54 @@ struct QRAFFLE : public ContractBase
27382771
if (state.get().registers.needsCleanup()) { state.mut().registers.cleanup(); }
27392772
}
27402773

2774+
MIGRATE()
2775+
{
2776+
auto& s = state.mut();
2777+
2778+
copyMemory(s.registers, oldState.registers);
2779+
copyMemory(s.voteParticipation, oldState.voteParticipation);
2780+
copyMemory(s.voteValues, oldState.voteValues);
2781+
copyMemory(s.quRaffleMemberSet, oldState.quRaffleMemberSet);
2782+
copyMemory(s.tokenRaffleParticipation, oldState.tokenRaffleParticipation);
2783+
copyMemory(s.quRaffleEntryAmount, oldState.quRaffleEntryAmount);
2784+
copyMemory(s.proposalsPerProposer, oldState.proposalsPerProposer);
2785+
2786+
s.proposals = oldState.proposals;
2787+
s.numberOfVotedInProposal = oldState.numberOfVotedInProposal;
2788+
s.quRaffleMembers = oldState.quRaffleMembers;
2789+
s.activeTokenRaffle = oldState.activeTokenRaffle;
2790+
s.tokenRaffleMemberSlots = oldState.tokenRaffleMemberSlots;
2791+
s.numberOfTokenRaffleMembers = oldState.numberOfTokenRaffleMembers;
2792+
s.QuRaffles = oldState.QuRaffles;
2793+
s.tokenRaffle = oldState.tokenRaffle;
2794+
s.daoMemberCount = oldState.daoMemberCount;
2795+
2796+
s.initialRegister1 = oldState.initialRegister1;
2797+
s.initialRegister2 = oldState.initialRegister2;
2798+
s.initialRegister3 = oldState.initialRegister3;
2799+
s.initialRegister4 = oldState.initialRegister4;
2800+
s.initialRegister5 = oldState.initialRegister5;
2801+
s.charityAddress = oldState.charityAddress;
2802+
s.feeAddress = oldState.feeAddress;
2803+
s.QXMRIssuer = oldState.QXMRIssuer;
2804+
s.epochRevenue = oldState.epochRevenue;
2805+
s.epochQXMRRevenue = oldState.epochQXMRRevenue;
2806+
s.qREAmount = oldState.qREAmount;
2807+
s.totalBurnAmount = oldState.totalBurnAmount;
2808+
s.totalCharityAmount = oldState.totalCharityAmount;
2809+
s.totalShareholderAmount = oldState.totalShareholderAmount;
2810+
s.totalRegisterAmount = oldState.totalRegisterAmount;
2811+
s.totalFeeAmount = oldState.totalFeeAmount;
2812+
s.totalWinnerAmount = oldState.totalWinnerAmount;
2813+
s.largestWinnerAmount = oldState.largestWinnerAmount;
2814+
s.numberOfRegisters = oldState.numberOfRegisters;
2815+
s.numberOfQuRaffleMembers = oldState.numberOfQuRaffleMembers;
2816+
s.numberOfEntryAmountSubmitted = oldState.numberOfEntryAmountSubmitted;
2817+
s.numberOfProposals = oldState.numberOfProposals;
2818+
s.numberOfActiveTokenRaffle = oldState.numberOfActiveTokenRaffle;
2819+
s.numberOfEndedTokenRaffle = oldState.numberOfEndedTokenRaffle;
2820+
}
2821+
27412822
PRE_ACQUIRE_SHARES()
27422823
{
27432824
// Accept all incoming share management transfers for free.

test/contract_qraffle.cpp

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4192,4 +4192,228 @@ TEST(ContractQraffle, AssetRaffle_MultipleEpochs_PerCreatorCounterReset)
41924192
makeBundle(t201, 100));
41934193
EXPECT_EQ(r.returnCode, QRAFFLE_SUCCESS) << "epoch 201 raffle " << i;
41944194
}
4195-
}
4195+
}
4196+
4197+
// ── TEST: MIGRATE_PreservesAllOldFieldsAndZeroesNew ───────────────────────────
4198+
// Direct unit test for QRAFFLE::__impl_migrate. Builds a populated OldStateData
4199+
// (every preserved field gets a unique sentinel value), zeroes the new state
4200+
// buffer to mimic what core does before invoking MIGRATE, calls the migration,
4201+
// then asserts:
4202+
// 1. every preserved scalar / Array / HashMap / HashSet field round-trips,
4203+
// 2. shareholdersList content is intentionally dropped,
4204+
// 3. every new asset-raffle field is zero (i.e., MIGRATE didn't touch them).
4205+
//
4206+
// MIGRATE state buffers are large (multi-MB); both old and new state are heap-
4207+
// allocated to avoid blowing the test stack.
4208+
TEST(ContractQraffle, MIGRATE_PreservesAllOldFieldsAndZeroesNew)
4209+
{
4210+
ContractTestingQraffle qraffle; // sets up contractStates[QRAFFLE_CONTRACT_INDEX] and runs INITIALIZE
4211+
4212+
// ── Build a populated OldStateData on the heap. ───────────────────────────
4213+
auto* oldStatePtr = (QRAFFLE::OldStateData*)malloc(sizeof(QRAFFLE::OldStateData));
4214+
ASSERT_NE(oldStatePtr, nullptr);
4215+
setMem(oldStatePtr, sizeof(QRAFFLE::OldStateData), 0);
4216+
QRAFFLE::OldStateData& oldState = *oldStatePtr;
4217+
4218+
// Sentinel ids — distinct so any mis-routing in MIGRATE produces a visible mismatch.
4219+
const id senReg1 = id(0x1001, 0x1002, 0x1003, 0x1004);
4220+
const id senReg2 = id(0x2001, 0x2002, 0x2003, 0x2004);
4221+
const id senReg3 = id(0x3001, 0x3002, 0x3003, 0x3004);
4222+
const id senReg4 = id(0x4001, 0x4002, 0x4003, 0x4004);
4223+
const id senReg5 = id(0x5001, 0x5002, 0x5003, 0x5004);
4224+
const id senCharity = id(0xC001, 0xC002, 0xC003, 0xC004);
4225+
const id senFee = id(0xF001, 0xF002, 0xF003, 0xF004);
4226+
const id senIssuer = id(0xA001, 0xA002, 0xA003, 0xA004);
4227+
const id senVoter = id(0xB001, 0xB002, 0xB003, 0xB004);
4228+
const id senQuMember = id(0xD001, 0xD002, 0xD003, 0xD004);
4229+
const id senTokenMem = id(0xE001, 0xE002, 0xE003, 0xE004);
4230+
const id senProposer = id(0x9001, 0x9002, 0x9003, 0x9004);
4231+
const id senQuEntryUser = id(0x8001, 0x8002, 0x8003, 0x8004);
4232+
const id senDeadField = id(0xDEAD, 0xDEAD, 0xDEAD, 0xDEAD); // for shareholdersList — must NOT migrate
4233+
4234+
// Scalars: assign uniquely-recognizable values.
4235+
oldState.initialRegister1 = senReg1;
4236+
oldState.initialRegister2 = senReg2;
4237+
oldState.initialRegister3 = senReg3;
4238+
oldState.initialRegister4 = senReg4;
4239+
oldState.initialRegister5 = senReg5;
4240+
oldState.charityAddress = senCharity;
4241+
oldState.feeAddress = senFee;
4242+
oldState.QXMRIssuer = senIssuer;
4243+
oldState.epochRevenue = 1'000'001ull;
4244+
oldState.epochQXMRRevenue = 1'000'002ull;
4245+
oldState.qREAmount = 1'000'003ull;
4246+
oldState.totalBurnAmount = 1'000'004ull;
4247+
oldState.totalCharityAmount = 1'000'005ull;
4248+
oldState.totalShareholderAmount = 1'000'006ull;
4249+
oldState.totalRegisterAmount = 1'000'007ull;
4250+
oldState.totalFeeAmount = 1'000'008ull;
4251+
oldState.totalWinnerAmount = 1'000'009ull;
4252+
oldState.largestWinnerAmount = 1'000'010ull;
4253+
oldState.numberOfRegisters = 7u;
4254+
oldState.numberOfQuRaffleMembers = 8u;
4255+
oldState.numberOfEntryAmountSubmitted = 9u;
4256+
oldState.numberOfProposals = 10u;
4257+
oldState.numberOfActiveTokenRaffle = 11u;
4258+
oldState.numberOfEndedTokenRaffle = 12u;
4259+
4260+
// HashMaps / HashSets: one sentinel entry each.
4261+
oldState.registers.set(senReg1, /*tokenType=*/1u);
4262+
oldState.quRaffleMemberSet.add(senQuMember);
4263+
oldState.quRaffleEntryAmount.set(senQuEntryUser, 42'424'242ull);
4264+
oldState.proposalsPerProposer.set(senProposer, 3u);
4265+
4266+
QPI::BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH> participation; participation.setAll(0); participation.set(2, 1);
4267+
QPI::BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH> values; values.setAll(0); values.set(2, 1);
4268+
oldState.voteParticipation.set(senVoter, participation);
4269+
oldState.voteValues.set(senVoter, values);
4270+
4271+
QPI::BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH> tokenPart; tokenPart.setAll(0); tokenPart.set(4, 1);
4272+
oldState.tokenRaffleParticipation.set(senTokenMem, tokenPart);
4273+
4274+
// shareholdersList: dropped in MIGRATE — populate to prove we don't accidentally migrate it.
4275+
oldState.shareholdersList.add(senDeadField);
4276+
4277+
// Arrays: one sentinel entry per array.
4278+
QRAFFLE::ProposalInfo p{};
4279+
p.token.assetName = 0xAA55AAull; p.token.issuer = senIssuer;
4280+
p.proposer = senProposer; p.entryAmount = 7'654'321ull; p.nYes = 13u; p.nNo = 5u;
4281+
oldState.proposals.set(3, p);
4282+
oldState.numberOfVotedInProposal.set(3, 18u);
4283+
4284+
oldState.quRaffleMembers.set(0, senQuMember);
4285+
4286+
QRAFFLE::ActiveTokenRaffleInfo ar{};
4287+
ar.token.assetName = 0xBB66BBull; ar.token.issuer = senIssuer; ar.entryAmount = 9'999ull;
4288+
oldState.activeTokenRaffle.set(2, ar);
4289+
4290+
oldState.tokenRaffleMemberSlots.set(0, senTokenMem);
4291+
oldState.numberOfTokenRaffleMembers.set(2, 1u);
4292+
4293+
QRAFFLE::QuRaffleInfo qri{};
4294+
qri.epochWinner = senQuMember; qri.receivedAmount = 11'111'111ull;
4295+
qri.entryAmount = 100u; qri.numberOfMembers = 1u; qri.winnerIndex = 0u;
4296+
oldState.QuRaffles.set(100, qri);
4297+
4298+
QRAFFLE::TokenRaffleInfo tri{};
4299+
tri.epochWinner = senTokenMem; tri.token.assetName = 0xCC77CCull; tri.token.issuer = senIssuer;
4300+
tri.entryAmount = 50u; tri.numberOfMembers = 1u; tri.winnerIndex = 0u; tri.epoch = 199u;
4301+
oldState.tokenRaffle.set(7, tri);
4302+
4303+
oldState.daoMemberCount.set(100, 7u);
4304+
4305+
// ── Pre-zero the new state buffer (mimics core's setMem before MIGRATE). ──
4306+
setMem(contractStates[QRAFFLE_CONTRACT_INDEX], sizeof(QRAFFLE::StateData), 0);
4307+
4308+
// ── Invoke __impl_migrate directly. ───────────────────────────────────────
4309+
QpiContextUserFunctionCall qpiCtx(QRAFFLE_CONTRACT_INDEX);
4310+
auto& state = *reinterpret_cast<QPI::ContractState<QRAFFLE::StateData, QRAFFLE_CONTRACT_INDEX>*>(
4311+
contractStates[QRAFFLE_CONTRACT_INDEX]);
4312+
QRAFFLE::MIGRATE_locals locals;
4313+
QRAFFLE::__impl_migrate(qpiCtx, state, oldState, locals);
4314+
4315+
// ── Assert preserved fields round-tripped. ────────────────────────────────
4316+
QRaffleChecker* s = qraffle.getState();
4317+
4318+
// Scalars
4319+
EXPECT_EQ(s->initialRegister1, senReg1);
4320+
EXPECT_EQ(s->initialRegister2, senReg2);
4321+
EXPECT_EQ(s->initialRegister3, senReg3);
4322+
EXPECT_EQ(s->initialRegister4, senReg4);
4323+
EXPECT_EQ(s->initialRegister5, senReg5);
4324+
EXPECT_EQ(s->charityAddress, senCharity);
4325+
EXPECT_EQ(s->feeAddress, senFee);
4326+
EXPECT_EQ(s->QXMRIssuer, senIssuer);
4327+
EXPECT_EQ(s->epochRevenue, 1'000'001ull);
4328+
EXPECT_EQ(s->epochQXMRRevenue, 1'000'002ull);
4329+
EXPECT_EQ(s->qREAmount, 1'000'003ull);
4330+
EXPECT_EQ(s->totalBurnAmount, 1'000'004ull);
4331+
EXPECT_EQ(s->totalCharityAmount, 1'000'005ull);
4332+
EXPECT_EQ(s->totalShareholderAmount, 1'000'006ull);
4333+
EXPECT_EQ(s->totalRegisterAmount, 1'000'007ull);
4334+
EXPECT_EQ(s->totalFeeAmount, 1'000'008ull);
4335+
EXPECT_EQ(s->totalWinnerAmount, 1'000'009ull);
4336+
EXPECT_EQ(s->largestWinnerAmount, 1'000'010ull);
4337+
EXPECT_EQ(s->numberOfRegisters, 7u);
4338+
EXPECT_EQ(s->numberOfQuRaffleMembers, 8u);
4339+
EXPECT_EQ(s->numberOfEntryAmountSubmitted, 9u);
4340+
EXPECT_EQ(s->numberOfProposals, 10u);
4341+
EXPECT_EQ(s->numberOfActiveTokenRaffle, 11u);
4342+
EXPECT_EQ(s->numberOfEndedTokenRaffle, 12u);
4343+
4344+
// HashMaps / HashSets
4345+
EXPECT_TRUE(s->registers.contains(senReg1));
4346+
uint8 regType = 0; s->registers.get(senReg1, regType);
4347+
EXPECT_EQ(regType, 1u);
4348+
4349+
EXPECT_TRUE(s->quRaffleMemberSet.contains(senQuMember));
4350+
4351+
EXPECT_TRUE(s->quRaffleEntryAmount.contains(senQuEntryUser));
4352+
uint64 entryAmt = 0; s->quRaffleEntryAmount.get(senQuEntryUser, entryAmt);
4353+
EXPECT_EQ(entryAmt, 42'424'242ull);
4354+
4355+
EXPECT_TRUE(s->proposalsPerProposer.contains(senProposer));
4356+
uint8 ppCount = 0; s->proposalsPerProposer.get(senProposer, ppCount);
4357+
EXPECT_EQ(ppCount, 3u);
4358+
4359+
QPI::BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH> partOut; partOut.setAll(0);
4360+
EXPECT_TRUE(s->voteParticipation.get(senVoter, partOut));
4361+
EXPECT_EQ(partOut.get(2), 1);
4362+
4363+
QPI::BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH> valOut; valOut.setAll(0);
4364+
EXPECT_TRUE(s->voteValues.get(senVoter, valOut));
4365+
EXPECT_EQ(valOut.get(2), 1);
4366+
4367+
QPI::BitArray<QRAFFLE_MAX_PROPOSAL_EPOCH> tokPartOut; tokPartOut.setAll(0);
4368+
EXPECT_TRUE(s->tokenRaffleParticipation.get(senTokenMem, tokPartOut));
4369+
EXPECT_EQ(tokPartOut.get(4), 1);
4370+
4371+
// Arrays
4372+
EXPECT_EQ(s->proposals.get(3).token.assetName, 0xAA55AAull);
4373+
EXPECT_EQ(s->proposals.get(3).token.issuer, senIssuer);
4374+
EXPECT_EQ(s->proposals.get(3).proposer, senProposer);
4375+
EXPECT_EQ(s->proposals.get(3).entryAmount, 7'654'321ull);
4376+
EXPECT_EQ(s->proposals.get(3).nYes, 13u);
4377+
EXPECT_EQ(s->proposals.get(3).nNo, 5u);
4378+
EXPECT_EQ(s->numberOfVotedInProposal.get(3), 18u);
4379+
4380+
EXPECT_EQ(s->quRaffleMembers.get(0), senQuMember);
4381+
EXPECT_EQ(s->activeTokenRaffle.get(2).token.assetName, 0xBB66BBull);
4382+
EXPECT_EQ(s->activeTokenRaffle.get(2).entryAmount, 9'999ull);
4383+
EXPECT_EQ(s->tokenRaffleMemberSlots.get(0), senTokenMem);
4384+
EXPECT_EQ(s->numberOfTokenRaffleMembers.get(2), 1u);
4385+
4386+
EXPECT_EQ(s->QuRaffles.get(100).epochWinner, senQuMember);
4387+
EXPECT_EQ(s->QuRaffles.get(100).receivedAmount, 11'111'111ull);
4388+
4389+
EXPECT_EQ(s->tokenRaffle.get(7).epochWinner, senTokenMem);
4390+
EXPECT_EQ(s->tokenRaffle.get(7).token.assetName, 0xCC77CCull);
4391+
EXPECT_EQ(s->tokenRaffle.get(7).epoch, 199u);
4392+
4393+
EXPECT_EQ(s->daoMemberCount.get(100), 7u);
4394+
4395+
// ── Assert ALL new asset-raffle fields are zero. ──────────────────────────
4396+
EXPECT_EQ(s->numberOfActiveAssetRaffles, 0u);
4397+
EXPECT_EQ(s->numberOfEndedAssetRaffles, 0u);
4398+
EXPECT_EQ(s->epochAssetRaffleDaoBucket, 0ull);
4399+
EXPECT_EQ(s->totalAssetRaffleProposalFees, 0ull);
4400+
EXPECT_EQ(s->totalAssetRaffleCreatorPaid, 0ull);
4401+
EXPECT_EQ(s->totalAssetRaffleRefunded, 0ull);
4402+
EXPECT_EQ(s->totalAssetRafflesCreated, 0u);
4403+
EXPECT_EQ(s->totalAssetRafflesSucceeded, 0u);
4404+
EXPECT_EQ(s->totalAssetRafflesFailed, 0u);
4405+
// assetRafflesPerCreator and assetRaffleParticipation are HashMaps; "zero" means empty.
4406+
EXPECT_EQ(s->assetRafflesPerCreator.population(), 0u);
4407+
EXPECT_EQ(s->assetRaffleParticipation.population(), 0u);
4408+
EXPECT_EQ(s->assetRaffleBuyerSlotIndex.population(), 0u);
4409+
// Sample one slot from each new Array to confirm zero (full-array equality would be wasteful).
4410+
EXPECT_EQ(s->activeAssetRaffles.get(0).creator, id(0,0,0,0));
4411+
EXPECT_EQ(s->activeAssetRaffles.get(0).reservePriceQu, 0ull);
4412+
EXPECT_EQ(s->activeAssetRaffleItems.get(0).numberOfShares, 0);
4413+
EXPECT_EQ(s->activeAssetRaffleBuyers.get(0), id(0,0,0,0));
4414+
EXPECT_EQ(s->activeAssetRaffleBuyerTickets.get(0), 0u);
4415+
EXPECT_EQ(s->endedAssetRaffles.get(0).creator, id(0,0,0,0));
4416+
EXPECT_EQ(s->endedAssetRaffles.get(0).grossPoolQu, 0ull);
4417+
4418+
free(oldStatePtr);
4419+
}

0 commit comments

Comments
 (0)