arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/dbtoapistructs.go
api/dbtoapistructs.goRaw
  1package api
  2
  3import (
  4	"encoding/base64"
  5	"encoding/hex"
  6	"errors"
  7	"fmt"
  8	"math/big"
  9	"strconv"
 10	"strings"
 11	"time"
 12
 13	ethCommon "github.com/ethereum/go-ethereum/common"
 14	"github.com/hermeznetwork/hermez-node/common"
 15	"github.com/hermeznetwork/hermez-node/db"
 16	"github.com/hermeznetwork/hermez-node/db/historydb"
 17	"github.com/hermeznetwork/hermez-node/db/l2db"
 18	"github.com/hermeznetwork/hermez-node/eth"
 19	"github.com/iden3/go-iden3-crypto/babyjub"
 20)
 21
 22const exitIdx = "hez:EXIT:1"
 23
 24type errorMsg struct {
 25	Message string
 26}
 27
 28func bjjToString(bjj *babyjub.PublicKey) string {
 29	pkComp := [32]byte(bjj.Compress())
 30	sum := pkComp[0]
 31	for i := 1; i < len(pkComp); i++ {
 32		sum += pkComp[i]
 33	}
 34	bjjSum := append(pkComp[:], sum)
 35	return "hez:" + base64.RawURLEncoding.EncodeToString(bjjSum)
 36}
 37
 38func ethAddrToHez(addr ethCommon.Address) string {
 39	return "hez:" + addr.String()
 40}
 41
 42func idxToHez(idx common.Idx, tokenSymbol string) string {
 43	if idx == 1 {
 44		return exitIdx
 45	}
 46	return "hez:" + tokenSymbol + ":" + strconv.Itoa(int(idx))
 47}
 48
 49// History Tx
 50
 51type historyTxsAPI struct {
 52	Txs        []historyTxAPI `json:"transactions"`
 53	Pagination *db.Pagination `json:"pagination"`
 54}
 55
 56func (htx *historyTxsAPI) GetPagination() *db.Pagination {
 57	if htx.Txs[0].ItemID < htx.Txs[len(htx.Txs)-1].ItemID {
 58		htx.Pagination.FirstReturnedItem = htx.Txs[0].ItemID
 59		htx.Pagination.LastReturnedItem = htx.Txs[len(htx.Txs)-1].ItemID
 60	} else {
 61		htx.Pagination.LastReturnedItem = htx.Txs[0].ItemID
 62		htx.Pagination.FirstReturnedItem = htx.Txs[len(htx.Txs)-1].ItemID
 63	}
 64	return htx.Pagination
 65}
 66func (htx *historyTxsAPI) Len() int { return len(htx.Txs) }
 67
 68type l1Info struct {
 69	ToForgeL1TxsNum       *int64   `json:"toForgeL1TransactionsNum"`
 70	UserOrigin            bool     `json:"userOrigin"`
 71	FromEthAddr           string   `json:"fromHezEthereumAddress"`
 72	FromBJJ               string   `json:"fromBJJ"`
 73	LoadAmount            string   `json:"loadAmount"`
 74	HistoricLoadAmountUSD *float64 `json:"historicLoadAmountUSD"`
 75	EthBlockNum           int64    `json:"ethereumBlockNum"`
 76}
 77
 78type l2Info struct {
 79	Fee            common.FeeSelector `json:"fee"`
 80	HistoricFeeUSD *float64           `json:"historicFeeUSD"`
 81	Nonce          common.Nonce       `json:"nonce"`
 82}
 83
 84type historyTxAPI struct {
 85	IsL1        string           `json:"L1orL2"`
 86	TxID        common.TxID      `json:"id"`
 87	ItemID      int              `json:"itemId"`
 88	Type        common.TxType    `json:"type"`
 89	Position    int              `json:"position"`
 90	FromIdx     *string          `json:"fromAccountIndex"`
 91	ToIdx       string           `json:"toAccountIndex"`
 92	Amount      string           `json:"amount"`
 93	BatchNum    *common.BatchNum `json:"batchNum"`
 94	HistoricUSD *float64         `json:"historicUSD"`
 95	Timestamp   time.Time        `json:"timestamp"`
 96	L1Info      *l1Info          `json:"L1Info"`
 97	L2Info      *l2Info          `json:"L2Info"`
 98	Token       tokenAPI         `json:"token"`
 99}
100
101func historyTxsToAPI(dbTxs []historydb.HistoryTx) []historyTxAPI {
102	apiTxs := []historyTxAPI{}
103	for i := 0; i < len(dbTxs); i++ {
104		apiTx := historyTxAPI{
105			TxID:        dbTxs[i].TxID,
106			ItemID:      dbTxs[i].ItemID,
107			Type:        dbTxs[i].Type,
108			Position:    dbTxs[i].Position,
109			ToIdx:       idxToHez(dbTxs[i].ToIdx, dbTxs[i].TokenSymbol),
110			Amount:      dbTxs[i].Amount.String(),
111			HistoricUSD: dbTxs[i].HistoricUSD,
112			BatchNum:    dbTxs[i].BatchNum,
113			Timestamp:   dbTxs[i].Timestamp,
114			Token: tokenAPI{
115				TokenID:     dbTxs[i].TokenID,
116				EthBlockNum: dbTxs[i].TokenEthBlockNum,
117				EthAddr:     dbTxs[i].TokenEthAddr,
118				Name:        dbTxs[i].TokenName,
119				Symbol:      dbTxs[i].TokenSymbol,
120				Decimals:    dbTxs[i].TokenDecimals,
121				USD:         dbTxs[i].TokenUSD,
122				USDUpdate:   dbTxs[i].TokenUSDUpdate,
123			},
124			L1Info: nil,
125			L2Info: nil,
126		}
127		if dbTxs[i].FromIdx != nil {
128			fromIdx := new(string)
129			*fromIdx = idxToHez(*dbTxs[i].FromIdx, dbTxs[i].TokenSymbol)
130			apiTx.FromIdx = fromIdx
131		}
132		if dbTxs[i].IsL1 {
133			apiTx.IsL1 = "L1"
134			apiTx.L1Info = &l1Info{
135				ToForgeL1TxsNum:       dbTxs[i].ToForgeL1TxsNum,
136				UserOrigin:            *dbTxs[i].UserOrigin,
137				FromEthAddr:           ethAddrToHez(*dbTxs[i].FromEthAddr),
138				FromBJJ:               bjjToString(dbTxs[i].FromBJJ),
139				LoadAmount:            dbTxs[i].LoadAmount.String(),
140				HistoricLoadAmountUSD: dbTxs[i].HistoricLoadAmountUSD,
141				EthBlockNum:           dbTxs[i].EthBlockNum,
142			}
143		} else {
144			apiTx.IsL1 = "L2"
145			apiTx.L2Info = &l2Info{
146				Fee:            *dbTxs[i].Fee,
147				HistoricFeeUSD: dbTxs[i].HistoricFeeUSD,
148				Nonce:          *dbTxs[i].Nonce,
149			}
150		}
151		apiTxs = append(apiTxs, apiTx)
152	}
153	return apiTxs
154}
155
156// Exit
157
158type exitsAPI struct {
159	Exits      []exitAPI      `json:"exits"`
160	Pagination *db.Pagination `json:"pagination"`
161}
162
163func (e *exitsAPI) GetPagination() *db.Pagination {
164	if e.Exits[0].ItemID < e.Exits[len(e.Exits)-1].ItemID {
165		e.Pagination.FirstReturnedItem = e.Exits[0].ItemID
166		e.Pagination.LastReturnedItem = e.Exits[len(e.Exits)-1].ItemID
167	} else {
168		e.Pagination.LastReturnedItem = e.Exits[0].ItemID
169		e.Pagination.FirstReturnedItem = e.Exits[len(e.Exits)-1].ItemID
170	}
171	return e.Pagination
172}
173func (e *exitsAPI) Len() int { return len(e.Exits) }
174
175type merkleProofAPI struct {
176	Root     string
177	Siblings []string
178	OldKey   string
179	OldValue string
180	IsOld0   bool
181	Key      string
182	Value    string
183	Fnc      int
184}
185
186type exitAPI struct {
187	ItemID                 int             `json:"itemId"`
188	BatchNum               common.BatchNum `json:"batchNum"`
189	AccountIdx             string          `json:"accountIndex"`
190	MerkleProof            merkleProofAPI  `json:"merkleProof"`
191	Balance                string          `json:"balance"`
192	InstantWithdrawn       *int64          `json:"instantWithdrawn"`
193	DelayedWithdrawRequest *int64          `json:"delayedWithdrawRequest"`
194	DelayedWithdrawn       *int64          `json:"delayedWithdrawn"`
195	Token                  tokenAPI        `json:"token"`
196}
197
198func historyExitsToAPI(dbExits []historydb.HistoryExit) []exitAPI {
199	apiExits := []exitAPI{}
200	for i := 0; i < len(dbExits); i++ {
201		exit := exitAPI{
202			ItemID:     dbExits[i].ItemID,
203			BatchNum:   dbExits[i].BatchNum,
204			AccountIdx: idxToHez(dbExits[i].AccountIdx, dbExits[i].TokenSymbol),
205			MerkleProof: merkleProofAPI{
206				Root:     dbExits[i].MerkleProof.Root.String(),
207				OldKey:   dbExits[i].MerkleProof.OldKey.String(),
208				OldValue: dbExits[i].MerkleProof.OldValue.String(),
209				IsOld0:   dbExits[i].MerkleProof.IsOld0,
210				Key:      dbExits[i].MerkleProof.Key.String(),
211				Value:    dbExits[i].MerkleProof.Value.String(),
212				Fnc:      dbExits[i].MerkleProof.Fnc,
213			},
214			Balance:                dbExits[i].Balance.String(),
215			InstantWithdrawn:       dbExits[i].InstantWithdrawn,
216			DelayedWithdrawRequest: dbExits[i].DelayedWithdrawRequest,
217			DelayedWithdrawn:       dbExits[i].DelayedWithdrawn,
218			Token: tokenAPI{
219				TokenID:     dbExits[i].TokenID,
220				EthBlockNum: dbExits[i].TokenEthBlockNum,
221				EthAddr:     dbExits[i].TokenEthAddr,
222				Name:        dbExits[i].TokenName,
223				Symbol:      dbExits[i].TokenSymbol,
224				Decimals:    dbExits[i].TokenDecimals,
225				USD:         dbExits[i].TokenUSD,
226				USDUpdate:   dbExits[i].TokenUSDUpdate,
227			},
228		}
229		siblings := []string{}
230		for j := 0; j < len(dbExits[i].MerkleProof.Siblings); j++ {
231			siblings = append(siblings, dbExits[i].MerkleProof.Siblings[j].String())
232		}
233		exit.MerkleProof.Siblings = siblings
234		apiExits = append(apiExits, exit)
235	}
236	return apiExits
237}
238
239// Tokens
240type tokensAPI struct {
241	Tokens     []tokenAPI     `json:"tokens"`
242	Pagination *db.Pagination `json:"pagination"`
243}
244
245func (t *tokensAPI) GetPagination() *db.Pagination {
246	if t.Tokens[0].ItemID < t.Tokens[len(t.Tokens)-1].ItemID {
247		t.Pagination.FirstReturnedItem = t.Tokens[0].ItemID
248		t.Pagination.LastReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID
249	} else {
250		t.Pagination.LastReturnedItem = t.Tokens[0].ItemID
251		t.Pagination.FirstReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID
252	}
253	return t.Pagination
254}
255func (t *tokensAPI) Len() int { return len(t.Tokens) }
256
257type tokenAPI struct {
258	ItemID      int               `json:"itemId"`
259	TokenID     common.TokenID    `json:"id"`
260	EthBlockNum int64             `json:"ethereumBlockNum"` // Ethereum block number in which this token was registered
261	EthAddr     ethCommon.Address `json:"ethereumAddress"`
262	Name        string            `json:"name"`
263	Symbol      string            `json:"symbol"`
264	Decimals    uint64            `json:"decimals"`
265	USD         *float64          `json:"USD"`
266	USDUpdate   *time.Time        `json:"fiatUpdate"`
267}
268
269func tokensToAPI(dbTokens []historydb.TokenRead) []tokenAPI {
270	apiTokens := []tokenAPI{}
271	for i := 0; i < len(dbTokens); i++ {
272		apiTokens = append(apiTokens, tokenAPI{
273			ItemID:      dbTokens[i].ItemID,
274			TokenID:     dbTokens[i].TokenID,
275			EthBlockNum: dbTokens[i].EthBlockNum,
276			EthAddr:     dbTokens[i].EthAddr,
277			Name:        dbTokens[i].Name,
278			Symbol:      dbTokens[i].Symbol,
279			Decimals:    dbTokens[i].Decimals,
280			USD:         dbTokens[i].USD,
281			USDUpdate:   dbTokens[i].USDUpdate,
282		})
283	}
284	return apiTokens
285}
286
287// Config
288
289type rollupConstants struct {
290	PublicConstants         eth.RollupPublicConstants `json:"publicConstants"`
291	MaxFeeIdxCoordinator    int                       `json:"maxFeeIdxCoordinator"`
292	ReservedIdx             int                       `json:"reservedIdx"`
293	ExitIdx                 int                       `json:"exitIdx"`
294	LimitLoadAmount         *big.Int                  `json:"limitLoadAmount"`
295	LimitL2TransferAmount   *big.Int                  `json:"limitL2TransferAmount"`
296	LimitTokens             int                       `json:"limitTokens"`
297	L1CoordinatorTotalBytes int                       `json:"l1CoordinatorTotalBytes"`
298	L1UserTotalBytes        int                       `json:"l1UserTotalBytes"`
299	MaxL1UserTx             int                       `json:"maxL1UserTx"`
300	MaxL1Tx                 int                       `json:"maxL1Tx"`
301	InputSHAConstantBytes   int                       `json:"inputSHAConstantBytes"`
302	NumBuckets              int                       `json:"numBuckets"`
303	MaxWithdrawalDelay      int                       `json:"maxWithdrawalDelay"`
304	ExchangeMultiplier      int                       `json:"exchangeMultiplier"`
305}
306
307type configAPI struct {
308	RollupConstants   rollupConstants       `json:"hermez"`
309	AuctionConstants  eth.AuctionConstants  `json:"auction"`
310	WDelayerConstants eth.WDelayerConstants `json:"withdrawalDelayer"`
311}
312
313// PoolL2Tx
314
315type receivedPoolTx struct {
316	TxID        common.TxID           `json:"id" binding:"required"`
317	Type        common.TxType         `json:"type" binding:"required"`
318	TokenID     common.TokenID        `json:"tokenId"`
319	FromIdx     string                `json:"fromAccountIndex" binding:"required"`
320	ToIdx       *string               `json:"toAccountIndex"`
321	ToEthAddr   *string               `json:"toHezEthereumAddress"`
322	ToBJJ       *string               `json:"toBjj"`
323	Amount      string                `json:"amount" binding:"required"`
324	Fee         common.FeeSelector    `json:"fee"`
325	Nonce       common.Nonce          `json:"nonce"`
326	Signature   babyjub.SignatureComp `json:"signature" binding:"required"`
327	RqFromIdx   *string               `json:"requestFromAccountIndex"`
328	RqToIdx     *string               `json:"requestToAccountIndex"`
329	RqToEthAddr *string               `json:"requestToHezEthereumAddress"`
330	RqToBJJ     *string               `json:"requestToBjj"`
331	RqTokenID   *common.TokenID       `json:"requestTokenId"`
332	RqAmount    *string               `json:"requestAmount"`
333	RqFee       *common.FeeSelector   `json:"requestFee"`
334	RqNonce     *common.Nonce         `json:"requestNonce"`
335}
336
337func (tx *receivedPoolTx) toDBWritePoolL2Tx() (*l2db.PoolL2TxWrite, error) {
338	amount := new(big.Int)
339	amount.SetString(tx.Amount, 10)
340	txw := &l2db.PoolL2TxWrite{
341		TxID:      tx.TxID,
342		TokenID:   tx.TokenID,
343		Amount:    amount,
344		Fee:       tx.Fee,
345		Nonce:     tx.Nonce,
346		State:     common.PoolL2TxStatePending,
347		Signature: tx.Signature,
348		RqTokenID: tx.RqTokenID,
349		RqFee:     tx.RqFee,
350		RqNonce:   tx.RqNonce,
351		Type:      tx.Type,
352	}
353	// Check FromIdx (required)
354	fidx, err := stringToIdx(tx.FromIdx, "fromAccountIndex")
355	if err != nil {
356		return nil, err
357	}
358	if fidx == nil {
359		return nil, errors.New("invalid fromAccountIndex")
360	}
361	// Set FromIdx
362	txw.FromIdx = common.Idx(*fidx)
363	// Set AmountFloat
364	f := new(big.Float).SetInt(amount)
365	amountF, _ := f.Float64()
366	txw.AmountFloat = amountF
367	if amountF < 0 {
368		return nil, errors.New("amount must be positive")
369	}
370	// Check "to" fields, only one of: ToIdx, ToEthAddr, ToBJJ
371	if tx.ToIdx != nil { // Case: Tx with ToIdx setted
372		// Set ToIdx
373		tidxUint, err := stringToIdx(*tx.ToIdx, "toAccountIndex")
374		if err != nil || tidxUint == nil {
375			return nil, errors.New("invalid toAccountIndex")
376		}
377		tidx := common.Idx(*tidxUint)
378		txw.ToIdx = &tidx
379	} else if tx.ToBJJ != nil { // Case: Tx with ToBJJ setted
380		// tx.ToEthAddr must be equal to ethAddrWhenBJJLower or ethAddrWhenBJJUpper
381		if tx.ToEthAddr != nil {
382			toEthAddr, err := hezStringToEthAddr(*tx.ToEthAddr, "toHezEthereumAddress")
383			if err != nil || *toEthAddr != common.FFAddr {
384				return nil, fmt.Errorf("if toBjj is setted, toHezEthereumAddress must be hez:%s", common.FFAddr.Hex())
385			}
386		} else {
387			return nil, fmt.Errorf("if toBjj is setted, toHezEthereumAddress must be hez:%s and toAccountIndex must be null", common.FFAddr.Hex())
388		}
389		// Set ToEthAddr and ToBJJ
390		toBJJ, err := hezStringToBJJ(*tx.ToBJJ, "toBjj")
391		if err != nil || toBJJ == nil {
392			return nil, errors.New("invalid toBjj")
393		}
394		txw.ToBJJ = toBJJ
395		txw.ToEthAddr = &common.FFAddr
396	} else if tx.ToEthAddr != nil { // Case: Tx with ToEthAddr setted
397		// Set ToEthAddr
398		toEthAddr, err := hezStringToEthAddr(*tx.ToEthAddr, "toHezEthereumAddress")
399		if err != nil || toEthAddr == nil {
400			return nil, errors.New("invalid toHezEthereumAddress")
401		}
402		txw.ToEthAddr = toEthAddr
403	} else {
404		return nil, errors.New("one of toAccountIndex, toHezEthereumAddress or toBjj must be setted")
405	}
406	// Check "rq" fields
407	if tx.RqFromIdx != nil {
408		// check and set RqFromIdx
409		rqfidxUint, err := stringToIdx(tx.FromIdx, "requestFromAccountIndex")
410		if err != nil || rqfidxUint == nil {
411			return nil, errors.New("invalid requestFromAccountIndex")
412		}
413		// Set RqFromIdx
414		rqfidx := common.Idx(*rqfidxUint)
415		txw.RqFromIdx = &rqfidx
416		// Case: RqTx with RqToIdx setted
417		if tx.RqToIdx != nil {
418			// Set ToIdx
419			tidxUint, err := stringToIdx(*tx.RqToIdx, "requestToAccountIndex")
420			if err != nil || tidxUint == nil {
421				return nil, errors.New("invalid requestToAccountIndex")
422			}
423			tidx := common.Idx(*tidxUint)
424			txw.ToIdx = &tidx
425		} else if tx.RqToBJJ != nil { // Case: Tx with ToBJJ setted
426			// tx.ToEthAddr must be equal to ethAddrWhenBJJLower or ethAddrWhenBJJUpper
427			if tx.RqToEthAddr != nil {
428				rqEthAddr, err := hezStringToEthAddr(*tx.RqToEthAddr, "")
429				if err != nil || *rqEthAddr != common.FFAddr {
430					return nil, fmt.Errorf("if requestToBjj is setted, requestToHezEthereumAddress must be hez:%s", common.FFAddr.Hex())
431				}
432			} else {
433				return nil, fmt.Errorf("if requestToBjj is setted, toHezEthereumAddress must be hez:%s and requestToAccountIndex must be null", common.FFAddr.Hex())
434			}
435			// Set ToEthAddr and ToBJJ
436			rqToBJJ, err := hezStringToBJJ(*tx.RqToBJJ, "requestToBjj")
437			if err != nil || rqToBJJ == nil {
438				return nil, errors.New("invalid requestToBjj")
439			}
440			txw.RqToBJJ = rqToBJJ
441			txw.RqToEthAddr = &common.FFAddr
442		} else if tx.RqToEthAddr != nil { // Case: Tx with ToEthAddr setted
443			// Set ToEthAddr
444			rqToEthAddr, err := hezStringToEthAddr(*tx.ToEthAddr, "requestToHezEthereumAddress")
445			if err != nil || rqToEthAddr == nil {
446				return nil, errors.New("invalid requestToHezEthereumAddress")
447			}
448			txw.RqToEthAddr = rqToEthAddr
449		} else {
450			return nil, errors.New("one of requestToAccountIndex, requestToHezEthereumAddress or requestToBjj must be setted")
451		}
452		if tx.RqAmount == nil {
453			return nil, errors.New("requestAmount must be provided if other request fields are setted")
454		}
455		rqAmount := new(big.Int)
456		rqAmount.SetString(*tx.RqAmount, 10)
457		txw.RqAmount = rqAmount
458	} else if tx.RqToIdx != nil && tx.RqToEthAddr != nil && tx.RqToBJJ != nil &&
459		tx.RqTokenID != nil && tx.RqAmount != nil && tx.RqNonce != nil && tx.RqFee != nil {
460		// if tx.RqToIdx is not setted, tx.Rq* must be null as well
461		return nil, errors.New("if requestFromAccountIndex is setted, the rest of request fields must be null as well")
462	}
463
464	return txw, validatePoolL2TxWrite(txw)
465}
466
467func validatePoolL2TxWrite(txw *l2db.PoolL2TxWrite) error {
468	poolTx := common.PoolL2Tx{
469		TxID:      txw.TxID,
470		FromIdx:   txw.FromIdx,
471		ToBJJ:     txw.ToBJJ,
472		TokenID:   txw.TokenID,
473		Amount:    txw.Amount,
474		Fee:       txw.Fee,
475		Nonce:     txw.Nonce,
476		State:     txw.State,
477		Signature: txw.Signature,
478		RqToBJJ:   txw.RqToBJJ,
479		RqAmount:  txw.RqAmount,
480		Type:      txw.Type,
481	}
482	// ToIdx
483	if txw.ToIdx != nil {
484		poolTx.ToIdx = *txw.ToIdx
485	}
486	// ToEthAddr
487	if txw.ToEthAddr == nil {
488		poolTx.ToEthAddr = common.EmptyAddr
489	} else {
490		poolTx.ToEthAddr = *txw.ToEthAddr
491	}
492	// RqFromIdx
493	if txw.RqFromIdx != nil {
494		poolTx.RqFromIdx = *txw.RqFromIdx
495	}
496	// RqToIdx
497	if txw.RqToIdx != nil {
498		poolTx.RqToIdx = *txw.RqToIdx
499	}
500	// RqToEthAddr
501	if txw.RqToEthAddr == nil {
502		poolTx.RqToEthAddr = common.EmptyAddr
503	} else {
504		poolTx.RqToEthAddr = *txw.RqToEthAddr
505	}
506	// RqTokenID
507	if txw.RqTokenID != nil {
508		poolTx.RqTokenID = *txw.RqTokenID
509	}
510	// RqFee
511	if txw.RqFee != nil {
512		poolTx.RqFee = *txw.RqFee
513	}
514	// RqNonce
515	if txw.RqNonce != nil {
516		poolTx.RqNonce = *txw.RqNonce
517	}
518	// Check type and id
519	_, err := common.NewPoolL2Tx(&poolTx)
520	if err != nil {
521		return err
522	}
523	// Check signature
524	// Get public key
525	account, err := s.GetAccount(poolTx.FromIdx)
526	if err != nil {
527		return err
528	}
529	if !poolTx.VerifySignature(account.PublicKey) {
530		return errors.New("wrong signature")
531	}
532	return nil
533}
534
535type sendPoolTx struct {
536	TxID        common.TxID           `json:"id"`
537	Type        common.TxType         `json:"type"`
538	FromIdx     string                `json:"fromAccountIndex"`
539	ToIdx       *string               `json:"toAccountIndex"`
540	ToEthAddr   *string               `json:"toHezEthereumAddress"`
541	ToBJJ       *string               `json:"toBjj"`
542	Amount      string                `json:"amount"`
543	Fee         common.FeeSelector    `json:"fee"`
544	Nonce       common.Nonce          `json:"nonce"`
545	State       common.PoolL2TxState  `json:"state"`
546	Signature   babyjub.SignatureComp `json:"signature"`
547	Timestamp   time.Time             `json:"timestamp"`
548	BatchNum    *common.BatchNum      `json:"batchNum"`
549	RqFromIdx   *string               `json:"requestFromAccountIndex"`
550	RqToIdx     *string               `json:"requestToAccountIndex"`
551	RqToEthAddr *string               `json:"requestToHezEthereumAddress"`
552	RqToBJJ     *string               `json:"requestToBJJ"`
553	RqTokenID   *common.TokenID       `json:"requestTokenId"`
554	RqAmount    *string               `json:"requestAmount"`
555	RqFee       *common.FeeSelector   `json:"requestFee"`
556	RqNonce     *common.Nonce         `json:"requestNonce"`
557	Token       tokenAPI              `json:"token"`
558}
559
560func poolL2TxReadToSend(dbTx *l2db.PoolL2TxRead) *sendPoolTx {
561	tx := &sendPoolTx{
562		TxID:      dbTx.TxID,
563		Type:      dbTx.Type,
564		FromIdx:   idxToHez(dbTx.FromIdx, dbTx.TokenSymbol),
565		Amount:    dbTx.Amount.String(),
566		Fee:       dbTx.Fee,
567		Nonce:     dbTx.Nonce,
568		State:     dbTx.State,
569		Signature: dbTx.Signature,
570		Timestamp: dbTx.Timestamp,
571		BatchNum:  dbTx.BatchNum,
572		RqTokenID: dbTx.RqTokenID,
573		RqFee:     dbTx.RqFee,
574		RqNonce:   dbTx.RqNonce,
575		Token: tokenAPI{
576			TokenID:     dbTx.TokenID,
577			EthBlockNum: dbTx.TokenEthBlockNum,
578			EthAddr:     dbTx.TokenEthAddr,
579			Name:        dbTx.TokenName,
580			Symbol:      dbTx.TokenSymbol,
581			Decimals:    dbTx.TokenDecimals,
582			USD:         dbTx.TokenUSD,
583			USDUpdate:   dbTx.TokenUSDUpdate,
584		},
585	}
586	// ToIdx
587	if dbTx.ToIdx != nil {
588		toIdx := idxToHez(*dbTx.ToIdx, dbTx.TokenSymbol)
589		tx.ToIdx = &toIdx
590	}
591	// ToEthAddr
592	if dbTx.ToEthAddr != nil {
593		toEth := ethAddrToHez(*dbTx.ToEthAddr)
594		tx.ToEthAddr = &toEth
595	}
596	// ToBJJ
597	if dbTx.ToBJJ != nil {
598		toBJJ := bjjToString(dbTx.ToBJJ)
599		tx.ToBJJ = &toBJJ
600	}
601	// RqFromIdx
602	if dbTx.RqFromIdx != nil {
603		rqFromIdx := idxToHez(*dbTx.RqFromIdx, dbTx.TokenSymbol)
604		tx.RqFromIdx = &rqFromIdx
605	}
606	// RqToIdx
607	if dbTx.RqToIdx != nil {
608		rqToIdx := idxToHez(*dbTx.RqToIdx, dbTx.TokenSymbol)
609		tx.RqToIdx = &rqToIdx
610	}
611	// RqToEthAddr
612	if dbTx.RqToEthAddr != nil {
613		rqToEth := ethAddrToHez(*dbTx.RqToEthAddr)
614		tx.RqToEthAddr = &rqToEth
615	}
616	// RqToBJJ
617	if dbTx.RqToBJJ != nil {
618		rqToBJJ := bjjToString(dbTx.RqToBJJ)
619		tx.RqToBJJ = &rqToBJJ
620	}
621	// RqAmount
622	if dbTx.RqAmount != nil {
623		rqAmount := dbTx.RqAmount.String()
624		tx.RqAmount = &rqAmount
625	}
626	return tx
627}
628
629// Coordinators
630
631type coordinatorAPI struct {
632	ItemID      int               `json:"itemId"`
633	Bidder      ethCommon.Address `json:"bidderAddr"`
634	Forger      ethCommon.Address `json:"forgerAddr"`
635	EthBlockNum int64             `json:"ethereumBlock"`
636	URL         string            `json:"URL"`
637}
638
639type coordinatorsAPI struct {
640	Coordinators []coordinatorAPI `json:"coordinators"`
641	Pagination   *db.Pagination   `json:"pagination"`
642}
643
644func (t *coordinatorsAPI) GetPagination() *db.Pagination {
645	if t.Coordinators[0].ItemID < t.Coordinators[len(t.Coordinators)-1].ItemID {
646		t.Pagination.FirstReturnedItem = t.Coordinators[0].ItemID
647		t.Pagination.LastReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID
648	} else {
649		t.Pagination.LastReturnedItem = t.Coordinators[0].ItemID
650		t.Pagination.FirstReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID
651	}
652	return t.Pagination
653}
654
655func (t *coordinatorsAPI) Len() int { return len(t.Coordinators) }
656
657func coordinatorsToAPI(dbCoordinators []historydb.HistoryCoordinator) []coordinatorAPI {
658	apiCoordinators := []coordinatorAPI{}
659	for i := 0; i < len(dbCoordinators); i++ {
660		apiCoordinators = append(apiCoordinators, coordinatorAPI{
661			ItemID:      dbCoordinators[i].ItemID,
662			Bidder:      dbCoordinators[i].Bidder,
663			Forger:      dbCoordinators[i].Forger,
664			EthBlockNum: dbCoordinators[i].EthBlockNum,
665			URL:         dbCoordinators[i].URL,
666		})
667	}
668	return apiCoordinators
669}
670
671// AccountCreationAuth
672
673type accountCreationAuthAPI struct {
674	EthAddr   string    `json:"hezEthereumAddress" binding:"required"`
675	BJJ       string    `json:"bjj" binding:"required"`
676	Signature string    `json:"signature" binding:"required"`
677	Timestamp time.Time `json:"timestamp"`
678}
679
680func accountCreationAuthToAPI(dbAuth *common.AccountCreationAuth) *accountCreationAuthAPI {
681	return &accountCreationAuthAPI{
682		EthAddr:   ethAddrToHez(dbAuth.EthAddr),
683		BJJ:       bjjToString(dbAuth.BJJ),
684		Signature: "0x" + hex.EncodeToString(dbAuth.Signature),
685		Timestamp: dbAuth.Timestamp,
686	}
687}
688
689func accountCreationAuthAPIToCommon(apiAuth *accountCreationAuthAPI) (*common.AccountCreationAuth, error) {
690	ethAddr, err := hezStringToEthAddr(apiAuth.EthAddr, "hezEthereumAddress")
691	if err != nil {
692		return nil, err
693	}
694	bjj, err := hezStringToBJJ(apiAuth.BJJ, "bjj")
695	if err != nil {
696		return nil, err
697	}
698	without0x := strings.TrimPrefix(apiAuth.Signature, "0x")
699	s, err := hex.DecodeString(without0x)
700	if err != nil {
701		return nil, err
702	}
703	auth := &common.AccountCreationAuth{
704		EthAddr:   *ethAddr,
705		BJJ:       bjj,
706		Signature: s,
707		Timestamp: apiAuth.Timestamp,
708	}
709	if !auth.VerifySignature() {
710		return nil, errors.New("invalid signature")
711	}
712	return auth, nil
713}