diff --git a/pkg/eth/signatures.go b/pkg/eth/signatures.go index e271957..da5f014 100644 --- a/pkg/eth/signatures.go +++ b/pkg/eth/signatures.go @@ -21,22 +21,27 @@ func Sign(privKey *ecdsa.PrivateKey, hash util.Hash) (chain.Signature, error) { } func ValidateSignature(hash, signature []byte, address common.Address) error { + // 2 Us ethHash := util.GethHash(hash) + // 200 ns sigCopy := make([]byte, len(signature)) copy(sigCopy, signature) if len(sigCopy) == 65 && sigCopy[64] > 26 { sigCopy[64] -= 27 } + // 220 Us pubKey, err := crypto.SigToPub(ethHash, sigCopy) if err != nil { return err } + // 3 Us signatureAddress := crypto.PubkeyToAddress(*pubKey) if address != signatureAddress { return errors.New("invalid signature") } + return nil } diff --git a/pkg/service/mempool.go b/pkg/service/mempool.go index 1773d68..df7789e 100644 --- a/pkg/service/mempool.go +++ b/pkg/service/mempool.go @@ -30,6 +30,7 @@ type TxInclusionResponse struct { type Mempool struct { txReqs chan *txRequest + validatedTxReqs chan *txRequest quit chan bool flushReq chan flushSpendReq txPool []MempoolTx @@ -50,36 +51,58 @@ type flushSpendReq struct { func NewMempool(storage db.Storage, client eth.Client) *Mempool { return &Mempool{ - txReqs: make(chan *txRequest), - quit: make(chan bool), - flushReq: make(chan flushSpendReq), - txPool: make([]MempoolTx, 0), - poolSpends: make(map[string]bool), - storage: storage, - client: client, + txReqs: make(chan *txRequest), + validatedTxReqs: make(chan *txRequest), + quit: make(chan bool), + flushReq: make(chan flushSpendReq), + txPool: make([]MempoolTx, 0), + poolSpends: make(map[string]bool), + storage: storage, + client: client, } } +func (m *Mempool) validateTransactionRequest(req *txRequest) { + tx := req.tx + var err error + if tx.Body.IsDeposit() { + err = m.VerifyDepositTransaction(&tx) + } else { + // 300-500us + err = m.VerifySpendTransaction(&tx) + } + if err != nil { + mPoolLogger.WithFields(logrus.Fields{ + "hash": tx.Body.SignatureHash().Hex(), + "reason": err, + }).Warn("transaction rejected from mempool") + + req.res <- TxInclusionResponse{ + Error: err, + } + return + } + + m.validatedTxReqs <- req +} + func (m *Mempool) Start() error { go func() { for { select { case req := <-m.txReqs: - if len(m.txPool) == MaxMempoolSize { - req.res <- TxInclusionResponse{ - Error: errors.New("mempool is full"), - } - continue - } - - tx := req.tx - var err error - if tx.Body.IsDeposit() { - err = m.VerifyDepositTransaction(&tx) - } else { - err = m.VerifySpendTransaction(&tx) - } - if err != nil { + go m.validateTransactionRequest(req) + case req := <-m.validatedTxReqs: + if len(m.txPool) == MaxMempoolSize { + req.res <- TxInclusionResponse{ + Error: errors.New("mempool is full"), + } + continue + } + + tx := req.tx + + if err := m.ensureNoPoolSpend(&tx); err != nil { mPoolLogger.WithFields(logrus.Fields{ "hash": tx.Body.SignatureHash().Hex(), "reason": err, @@ -90,10 +113,13 @@ func (m *Mempool) Start() error { } continue } + m.txPool = append(m.txPool, MempoolTx{ Tx: tx, Response: req.res, }) + + // 3us m.updatePoolSpends(&tx) case req := <-m.flushReq: res := m.txPool @@ -134,18 +160,10 @@ func (m *Mempool) Append(tx chain.Transaction) TxInclusionResponse { } func (m *Mempool) VerifySpendTransaction(tx *chain.Transaction) (error) { - if err := m.ensureNoPoolSpend(tx); err != nil { - return err - } - return validation.ValidateSpendTransaction(m.storage, tx) } func (m *Mempool) VerifyDepositTransaction(tx *chain.Transaction) error { - if err := m.ensureNoPoolSpend(tx); err != nil { - return err - } - return validation.ValidateDepositTransaction(m.storage, m.client, tx) } diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 503f7f5..ab0e0f1 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -14,6 +14,7 @@ import ( ) func ValidateSpendTransaction(storage db.Storage, tx *chain.Transaction) (error) { + // 300 ns if tx.Body.Output0.Amount.Cmp(big.NewInt(0)) == -1 { return NewErrNegativeOutput(0) } @@ -21,6 +22,7 @@ func ValidateSpendTransaction(storage db.Storage, tx *chain.Transaction) (error) return NewErrNegativeOutput(1) } + // 20 Us prevTx0Conf, err := storage.FindTransactionByBlockNumTxIdx(tx.Body.Input0.BlockNumber, tx.Body.Input0.TransactionIndex) if err == leveldb.ErrNotFound { return NewErrTxNotFound(0, tx.Body.Input0.BlockNumber, tx.Body.Input0.TransactionIndex) @@ -28,17 +30,22 @@ func ValidateSpendTransaction(storage db.Storage, tx *chain.Transaction) (error) if err != nil { return err } + + // 10 Us prevTx0 := prevTx0Conf.Transaction if prevTx0Conf.ConfirmSigs[0] != tx.Body.Input0ConfirmSig { return NewErrConfirmSigMismatch(0) } sigHash0 := tx.Body.SignatureHash() + + // 200 Us prevTx0Output := prevTx0.Body.OutputAt(tx.Body.Input0.OutputIndex) err = eth.ValidateSignature(sigHash0, tx.Sigs[0][:], prevTx0Output.Owner) if err != nil { return NewErrInvalidSignature(0) } + // 450 ns totalInput := big.NewInt(0) totalInput = totalInput.Add(totalInput, prevTx0Output.Amount) @@ -60,7 +67,9 @@ func ValidateSpendTransaction(storage db.Storage, tx *chain.Transaction) (error) } prevTx1Output := prevTx1.Body.OutputAt(tx.Body.Input1.OutputIndex) sigHash1 := tx.Body.SignatureHash() + err = eth.ValidateSignature(sigHash1, tx.Sigs[1][:], prevTx1Output.Owner) + if err != nil { return NewErrInvalidSignature(1) } @@ -68,6 +77,7 @@ func ValidateSpendTransaction(storage db.Storage, tx *chain.Transaction) (error) totalInput = totalInput.Add(totalInput, prevTx1Output.Amount) } + // 600-900 ns totalOutput := big.NewInt(0) totalOutput = totalOutput.Add(totalOutput, tx.Body.Output0.Amount) totalOutput = totalOutput.Add(totalOutput, tx.Body.Fee) @@ -79,6 +89,7 @@ func ValidateSpendTransaction(storage db.Storage, tx *chain.Transaction) (error) return NewErrInputOutputValueMismatch(totalInput, totalOutput) } + // 20 Us isDoubleSpent, err := storage.IsDoubleSpent(tx) if err != nil { return err