Skip to content

Commit dbba45e

Browse files
authored
[protocol 3.6] Remove short storageID uses (#1727)
* [protocol 3.6] Remove short storageID uses * [protocol3.6] Formatting
1 parent 859026a commit dbba45e

File tree

15 files changed

+211
-409
lines changed

15 files changed

+211
-409
lines changed

packages/loopring_v3.js/src/exchange_v3.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,9 @@ export class ExchangeV3 {
703703
}
704704
}
705705
],
706-
"0x" + data/*transaction.input*/.slice(2 + 4 * 2)
706+
"0x" +
707+
data /*transaction.input*/
708+
.slice(2 + 4 * 2)
707709
);
708710
//console.log(decodedInputs);
709711
const numBlocks = decodedInputs[0].length;
@@ -889,8 +891,13 @@ export class ExchangeV3 {
889891
};
890892

891893
for (let i = 0; i < block.blockSize; i++) {
892-
const txData1 = data.extractData(offset + i * 25, 25);
893-
const txData2 = data.extractData(offset + block.blockSize * 25 + i * 43, 43);
894+
const size1 = 29;
895+
const size2 = 39;
896+
const txData1 = data.extractData(offset + i * size1, size1);
897+
const txData2 = data.extractData(
898+
offset + block.blockSize * size1 + i * size2,
899+
size2
900+
);
894901
const txData = new Bitstream(txData1 + txData2);
895902

896903
const txType = txData.extractUint8(0);

packages/loopring_v3.js/src/request_processors/spot_trade_processor.ts

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ import BN from "bn.js";
22
import { Bitstream } from "../bitstream";
33
import { Constants } from "../constants";
44
import { fromFloat } from "../float";
5-
import { AccountLeaf, BalanceLeaf, BlockContext, ExchangeState, SpotTrade } from "../types";
5+
import {
6+
AccountLeaf,
7+
BalanceLeaf,
8+
BlockContext,
9+
ExchangeState,
10+
SpotTrade
11+
} from "../types";
612

713
interface SettlementValues {
814
fillSA: BN;
@@ -20,14 +26,18 @@ interface SettlementValues {
2026
* Processes ring settlement requests.
2127
*/
2228
export class SpotTradeProcessor {
23-
public static process(state: ExchangeState, block: BlockContext, data: Bitstream) {
29+
public static process(
30+
state: ExchangeState,
31+
block: BlockContext,
32+
data: Bitstream
33+
) {
2434
let offset = 1;
2535

2636
// Storage IDs
27-
const tradeHistoryDataA = data.extractUint16(offset);
28-
offset += 2;
29-
const tradeHistoryDataB = data.extractUint16(offset);
30-
offset += 2;
37+
const storageIdA = data.extractUint32(offset);
38+
offset += 4;
39+
const storageIdB = data.extractUint32(offset);
40+
offset += 4;
3141

3242
// Accounts
3343
const accountIdA = data.extractUint32(offset);
@@ -54,16 +64,10 @@ export class SpotTradeProcessor {
5464
offset += 1;
5565

5666
// Further extraction of packed data
57-
const tradeHistorySlotA = tradeHistoryDataA & 0b0011111111111111;
58-
const overwriteTradeHistorySlotA =
59-
(tradeHistoryDataA & 0b0100000000000000) !== 0;
6067
const limitMaskA = orderDataA & 0b10000000;
6168
const feeBipsA = orderDataA & 0b00111111;
6269
const fillAmountBorSA = limitMaskA > 0;
6370

64-
const tradeHistorySlotB = tradeHistoryDataB & 0b0011111111111111;
65-
const overwriteTradeHistorySlotB =
66-
(tradeHistoryDataB & 0b0100000000000000) !== 0;
6771
const limitMaskB = orderDataB & 0b10000000;
6872
const feeBipsB = orderDataB & 0b00111111;
6973
const fillAmountBorSB = limitMaskB > 0;
@@ -72,9 +76,6 @@ export class SpotTradeProcessor {
7276
const fillSA = fromFloat(fFillSA, Constants.Float24Encoding);
7377
const fillSB = fromFloat(fFillSB, Constants.Float24Encoding);
7478

75-
let orderIdA: number = undefined;
76-
let orderIdB: number = undefined;
77-
7879
const s = this.calculateSettlementValues(
7980
block.protocolFeeTakerBips,
8081
block.protocolFeeMakerBips,
@@ -88,35 +89,33 @@ export class SpotTradeProcessor {
8889
{
8990
const accountA = state.getAccount(accountIdA);
9091
accountA.getBalance(tokenA).balance.isub(s.fillSA);
91-
accountA.getBalance(tokenB).balance.iadd(s.fillBA).isub(s.feeA);
92+
accountA
93+
.getBalance(tokenB)
94+
.balance.iadd(s.fillBA)
95+
.isub(s.feeA);
9296

93-
const tradeHistoryA = accountA.getBalance(tokenA).getStorage(tradeHistorySlotA);
94-
if (tradeHistoryA.storageID === 0) {
95-
tradeHistoryA.storageID = tradeHistorySlotA;
96-
}
97-
if (overwriteTradeHistorySlotA) {
98-
tradeHistoryA.storageID += Constants.NUM_STORAGE_SLOTS;
97+
const tradeHistoryA = accountA.getBalance(tokenA).getStorage(storageIdA);
98+
if (tradeHistoryA.storageID !== storageIdA) {
9999
tradeHistoryA.data = new BN(0);
100100
}
101+
tradeHistoryA.storageID = storageIdA;
101102
tradeHistoryA.data.iadd(fillAmountBorSA ? s.fillBA : s.fillSA);
102-
orderIdA = tradeHistoryA.storageID;
103103
}
104104
// Update accountB
105105
{
106106
const accountB = state.getAccount(accountIdB);
107107
accountB.getBalance(tokenB).balance.isub(s.fillSB);
108-
accountB.getBalance(tokenA).balance.iadd(s.fillBB).isub(s.feeB);
108+
accountB
109+
.getBalance(tokenA)
110+
.balance.iadd(s.fillBB)
111+
.isub(s.feeB);
109112

110-
const tradeHistoryB = accountB.getBalance(tokenB).getStorage(tradeHistorySlotB);
111-
if (tradeHistoryB.storageID === 0) {
112-
tradeHistoryB.storageID = tradeHistorySlotB;
113-
}
114-
if (overwriteTradeHistorySlotB) {
115-
tradeHistoryB.storageID += Constants.NUM_STORAGE_SLOTS;
113+
const tradeHistoryB = accountB.getBalance(tokenB).getStorage(storageIdB);
114+
if (tradeHistoryB.storageID !== storageIdB) {
116115
tradeHistoryB.data = new BN(0);
117116
}
117+
tradeHistoryB.storageID = storageIdB;
118118
tradeHistoryB.data.iadd(fillAmountBorSB ? s.fillBB : s.fillSB);
119-
orderIdB = tradeHistoryB.storageID;
120119
}
121120

122121
// Update protocol fee
@@ -126,26 +125,31 @@ export class SpotTradeProcessor {
126125

127126
// Update operator
128127
const operator = state.getAccount(block.operatorAccountID);
129-
operator.getBalance(tokenA).balance.iadd(s.feeB).isub(s.protocolFeeB);
130-
operator.getBalance(tokenB).balance.iadd(s.feeA).isub(s.protocolFeeA);
131-
128+
operator
129+
.getBalance(tokenA)
130+
.balance.iadd(s.feeB)
131+
.isub(s.protocolFeeB);
132+
operator
133+
.getBalance(tokenB)
134+
.balance.iadd(s.feeA)
135+
.isub(s.protocolFeeA);
132136

133137
// Create struct
134138
const trade: SpotTrade = {
135139
exchangeId: state.exchangeId,
136140
requestIdx: state.processedRequests.length,
137-
blockIdx: /*block.blockIdx*/0,
141+
blockIdx: /*block.blockIdx*/ 0,
138142

139143
accountIdA,
140-
orderIdA,
144+
orderIdA: storageIdA,
141145
fillAmountBorSA,
142146
tokenA,
143147
fillSA: s.fillSA,
144148
feeA: s.feeA,
145149
protocolFeeA: s.protocolFeeA,
146150

147151
accountIdB,
148-
orderIdB,
152+
orderIdB: storageIdB,
149153
fillAmountBorSB,
150154
tokenB,
151155
fillSB: s.fillSB,

packages/loopring_v3.js/src/request_processors/transfer_processor.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ interface Transfer {
1111
amount?: BN;
1212
feeTokenID?: number;
1313
fee?: BN;
14-
shortStorageID?: number;
1514
validUntil?: number;
1615
storageID?: number;
1716
from?: string;
@@ -23,7 +22,11 @@ interface Transfer {
2322
* Processes internal transfer requests.
2423
*/
2524
export class TransferProcessor {
26-
public static process(state: ExchangeState, block: BlockContext, txData: Bitstream) {
25+
public static process(
26+
state: ExchangeState,
27+
block: BlockContext,
28+
txData: Bitstream
29+
) {
2730
const transfer = this.extractData(txData);
2831

2932
const from = state.getAccount(transfer.accountFromID);
@@ -38,16 +41,10 @@ export class TransferProcessor {
3841
from.getBalance(transfer.feeTokenID).balance.isub(transfer.fee);
3942

4043
// Nonce
41-
const storageSlot = transfer.shortStorageID & 0b0011111111111111;
42-
const overwriteSlot = (transfer.shortStorageID & 0b0100000000000000) !== 0;
43-
const storage = from.getBalance(transfer.tokenID).getStorage(storageSlot);
44-
if (storage.storageID === 0) {
45-
storage.storageID = storageSlot;
46-
}
47-
if (overwriteSlot) {
48-
storage.storageID += Constants.NUM_STORAGE_SLOTS;
49-
storage.data = new BN(0);
50-
}
44+
const storage = from
45+
.getBalance(transfer.tokenID)
46+
.getStorage(transfer.storageID);
47+
storage.storageID = transfer.storageID;
5148
storage.data = new BN(1);
5249

5350
const operator = state.getAccount(block.operatorAccountID);
@@ -70,18 +67,22 @@ export class TransferProcessor {
7067
offset += 4;
7168
transfer.tokenID = data.extractUint16(offset);
7269
offset += 2;
73-
transfer.amount = fromFloat(data.extractUint24(offset), Constants.Float24Encoding);
70+
transfer.amount = fromFloat(
71+
data.extractUint24(offset),
72+
Constants.Float24Encoding
73+
);
7474
offset += 3;
7575
transfer.feeTokenID = data.extractUint16(offset);
7676
offset += 2;
77-
transfer.fee = fromFloat(data.extractUint16(offset), Constants.Float16Encoding);
77+
transfer.fee = fromFloat(
78+
data.extractUint16(offset),
79+
Constants.Float16Encoding
80+
);
7881
offset += 2;
79-
transfer.shortStorageID = data.extractUint16(offset);
80-
offset += 2;
81-
transfer.to = data.extractAddress(offset);
82-
offset += 20;
8382
transfer.storageID = data.extractUint32(offset);
8483
offset += 4;
84+
transfer.to = data.extractAddress(offset);
85+
offset += 20;
8586
transfer.from = data.extractAddress(offset);
8687
offset += 20;
8788

packages/loopring_v3.js/src/request_processors/withdrawal_processor.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ interface Withdrawal {
2323
* Processes internal transfer requests.
2424
*/
2525
export class WithdrawalProcessor {
26-
public static process(state: ExchangeState, block: BlockContext, txData: Bitstream) {
26+
public static process(
27+
state: ExchangeState,
28+
block: BlockContext,
29+
txData: Bitstream
30+
) {
2731
const withdrawal = this.extractData(txData);
2832

2933
const account = state.getAccount(withdrawal.accountID);
@@ -42,8 +46,9 @@ export class WithdrawalProcessor {
4246

4347
if (withdrawal.type === 0 || withdrawal.type === 1) {
4448
// Nonce
45-
const storageSlot = withdrawal.storageID % Constants.NUM_STORAGE_SLOTS;
46-
const storage = account.getBalance(withdrawal.tokenID).getStorage(storageSlot);
49+
const storage = account
50+
.getBalance(withdrawal.tokenID)
51+
.getStorage(withdrawal.storageID);
4752
storage.storageID = withdrawal.storageID;
4853
storage.data = new BN(1);
4954
}
@@ -67,7 +72,10 @@ export class WithdrawalProcessor {
6772
offset += 12;
6873
withdrawal.feeTokenID = data.extractUint16(offset);
6974
offset += 2;
70-
withdrawal.fee = fromFloat(data.extractUint16(offset), Constants.Float16Encoding);
75+
withdrawal.fee = fromFloat(
76+
data.extractUint16(offset),
77+
Constants.Float16Encoding
78+
);
7179
offset += 2;
7280
withdrawal.storageID = data.extractUint32(offset);
7381
offset += 4;

packages/loopring_v3/DESIGN.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,14 +228,14 @@ The `fillAmountBorS` parameter can be used to decide which amount is the limitin
228228
```
229229
- For both Orders:
230230
- Account ID: 4 bytes
231-
- Short StorageID: 2 bytes
231+
- Storage ID: 4 bytes
232232
- TokenS: 2 bytes
233233
- FillS: 3 bytes (24 bits, 19 bits for the mantissa part and 5 for the exponent part)
234234
- Order data (fillAmountBorS,feeBips): 1 byte
235235
```
236236

237-
- => **24 bytes/ring**
238-
- => Calldata cost: 24 \* 16 = **384 gas/ring**
237+
- => **28 bytes/ring**
238+
- => Calldata cost: 28 \* 16 = **448 gas/ring**
239239

240240
### Fee Model
241241

@@ -321,7 +321,7 @@ If the order never left the DEX and the user trusts the DEX, the order can simpl
321321

322322
### Storage
323323

324-
Every account has a storage tree with 2^14 leaves **for every token**. Which leaf is used for storing e.g., the trading history for an order is completely left up to the user, and we call this the **storageID**. The storageID is stored in a 32-bit value and works as a 2D nonce. We allow the user to overwrite the existing storage stored at `storageID % 2^14` if `order.storageID == storage.storageID + 2^14`. If `order.storageID < storage.storageID` the order is automatically canceled. If `order.storageID == storage.storageID` we use the data stored in the leaf. If `order.storageID > storage.storageID + 2^14`, the order cannot be used yet as the storageID can only be incremented by `2^14`. This allows the account to create 2^32 unique orders for every token, and the only limitation is that only 2^14 of these orders selling a certain token can be active at the same time.
324+
Every account has a storage tree with 2^14 leaves **for every token**. Which leaf is used for storing e.g., the trading history for an order is completely left up to the user, and we call this the **storageID**. The storageID is stored in a 32-bit value and works as a 2D nonce. We allow the user to overwrite the existing storage stored at `storageID % 2^14` if `order.storageID > storage.storageID`. If `order.storageID < storage.storageID` the order is automatically canceled. If `order.storageID == storage.storageID` we use the data stored in the leaf. This allows the account to create 2^32 unique orders for every token, and the only limitation is that only 2^14 of these orders selling a certain token can be active at the same time.
325325

326326
While this was done for performance reasons (so we do not have to have a storage tree with a large depth using the order hash as an address), this does open up some interesting possibilities.
327327

@@ -390,14 +390,13 @@ Not all features available for transfers using EdDSA signatures are available us
390390
- Amount: 3 bytes (24 bits, 19 bits for the mantissa part and 5 for the exponent part)
391391
- Fee token ID: 2 bytes
392392
- Fee amount: 2 bytes (16 bits, 11 bits for the mantissa part and 5 for the exponent part)
393-
- Short storageID: 2 bytes
393+
- StorageID: 4 bytes
394394
- To: 20 bytes (only set when transferring to a new account)
395-
- storageID: 4 bytes (only set for conditional transfers)
396395
- From: 20 bytes (only set for conditional transfers)
397396
```
398397

399-
- => **20 bytes/transfer** (in the most common case)
400-
- => Calldata cost: 20 \* 16 = **320 gas/transfer**
398+
- => **22 bytes/transfer** (in the most common case)
399+
- => Calldata cost: 22 \* 16 = **352 gas/transfer**
401400

402401
## Deposit
403402

packages/loopring_v3/circuit/Circuits/SpotTradeCircuit.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -333,12 +333,8 @@ class SpotTradeCircuit : public BaseTransactionCircuit
333333
const VariableArrayT getPublicData() const
334334
{
335335
return flattenReverse({
336-
VariableArrayT(1, state.constants._0),
337-
VariableArrayT(1, tradeHistory_A.getOverwrite()),
338-
subArray(orderA.storageID.bits, 0, NUM_BITS_STORAGE_ADDRESS),
339-
VariableArrayT(1, state.constants._0),
340-
VariableArrayT(1, tradeHistory_B.getOverwrite()),
341-
subArray(orderB.storageID.bits, 0, NUM_BITS_STORAGE_ADDRESS),
336+
orderA.storageID.bits,
337+
orderB.storageID.bits,
342338

343339
orderA.accountID.bits,
344340
orderB.accountID.bits,

packages/loopring_v3/circuit/Circuits/TransferCircuit.h

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ class TransferCircuit : public BaseTransactionCircuit
9595
OrGadget da_NeedsToAddress;
9696
ArrayTernaryGadget da_To;
9797
ArrayTernaryGadget da_From;
98-
ArrayTernaryGadget da_StorageID;
9998

10099
// Fee as float
101100
FloatGadget fFee;
@@ -271,12 +270,6 @@ class TransferCircuit : public BaseTransactionCircuit
271270
from.bits,
272271
VariableArrayT(NUM_BITS_ADDRESS, state.constants._0),
273272
FMT(prefix, ".da_From")),
274-
da_StorageID(
275-
pb,
276-
isConditional.result(),
277-
storageID.bits,
278-
VariableArrayT(NUM_BITS_STORAGEID, state.constants._0),
279-
FMT(prefix, ".da_StorageID")),
280273

281274
// Fee as float
282275
fFee(pb, state.constants, Float16Encoding, FMT(prefix, ".fFee")),
@@ -409,7 +402,6 @@ class TransferCircuit : public BaseTransactionCircuit
409402
da_NeedsToAddress.generate_r1cs_witness();
410403
da_To.generate_r1cs_witness();
411404
da_From.generate_r1cs_witness();
412-
da_StorageID.generate_r1cs_witness();
413405

414406
// Fee as float
415407
fFee.generate_r1cs_witness(toFloat(transfer.fee, Float16Encoding));
@@ -487,7 +479,6 @@ class TransferCircuit : public BaseTransactionCircuit
487479
da_NeedsToAddress.generate_r1cs_constraints();
488480
da_To.generate_r1cs_constraints();
489481
da_From.generate_r1cs_constraints();
490-
da_StorageID.generate_r1cs_constraints();
491482

492483
// Fee as float
493484
fFee.generate_r1cs_constraints();
@@ -516,9 +507,8 @@ class TransferCircuit : public BaseTransactionCircuit
516507
fAmount.bits(),
517508
feeTokenID.bits,
518509
fFee.bits(),
519-
nonce.getShortStorageID(),
510+
storageID.bits,
520511
da_To.result(),
521-
da_StorageID.result(),
522512
da_From.result()});
523513
}
524514
};

0 commit comments

Comments
 (0)