[NOD-404] Calculate mass in API server (#452)

* [NOD-404] Calculate mass in API server

* [NOD-404] Fix uninitialized maps

* [NOD-404] Use txID instead of prevDBTransactionsOutput.Transaction.TransactionID
This commit is contained in:
Ori Newman 2019-11-07 10:27:12 +02:00 committed by Dan Aharoni
parent a9ff9b0e70
commit 770dfd147d
3 changed files with 98 additions and 2 deletions

View File

@ -104,7 +104,7 @@ type TransactionInput struct {
Sequence uint64
}
// Address is the gorm model for the 'utxos' table
// Address is the gorm model for the 'addresses' table
type Address struct {
ID uint64 `gorm:"primary_key"`
Address string

View File

@ -7,11 +7,14 @@ import (
"github.com/daglabs/btcd/apiserver/database"
"github.com/daglabs/btcd/apiserver/dbmodels"
"github.com/daglabs/btcd/apiserver/jsonrpc"
"github.com/daglabs/btcd/blockdag"
"github.com/daglabs/btcd/btcjson"
"github.com/daglabs/btcd/httpserverutils"
"github.com/daglabs/btcd/txscript"
"github.com/daglabs/btcd/util"
"github.com/daglabs/btcd/util/daghash"
"github.com/daglabs/btcd/util/subnetworkid"
"github.com/daglabs/btcd/wire"
"github.com/jinzhu/gorm"
"github.com/pkg/errors"
"strconv"
@ -269,6 +272,7 @@ func addBlock(client *jsonrpc.Client, block string, rawBlock btcjson.GetBlockVer
return err
}
blockMass := uint64(0)
for i, transaction := range rawBlock.RawTx {
dbSubnetwork, err := insertSubnetwork(dbTx, &transaction, client)
if err != nil {
@ -278,6 +282,7 @@ func addBlock(client *jsonrpc.Client, block string, rawBlock btcjson.GetBlockVer
if err != nil {
return err
}
blockMass += dbTransaction.Mass
err = insertTransactionBlock(dbTx, dbBlock, dbTransaction, uint32(i))
if err != nil {
return err
@ -292,6 +297,13 @@ func addBlock(client *jsonrpc.Client, block string, rawBlock btcjson.GetBlockVer
}
}
dbBlock.Mass = blockMass
dbResult := dbTx.Save(dbBlock)
dbErrors := dbResult.GetErrors()
if httpserverutils.HasDBError(dbErrors) {
return httpserverutils.NewErrorFromDBErrors("failed to update block: ", dbErrors)
}
dbTx.Commit()
return nil
}
@ -416,6 +428,10 @@ func insertTransaction(dbTx *gorm.DB, transaction *btcjson.TxRawResult, dbSubnet
return nil, httpserverutils.NewErrorFromDBErrors("failed to find transaction: ", dbErrors)
}
if httpserverutils.IsDBRecordNotFoundError(dbErrors) {
mass, err := calcTxMass(dbTx, transaction)
if err != nil {
return nil, err
}
payload, err := hex.DecodeString(transaction.Payload)
if err != nil {
return nil, err
@ -428,6 +444,7 @@ func insertTransaction(dbTx *gorm.DB, transaction *btcjson.TxRawResult, dbSubnet
Gas: transaction.Gas,
PayloadHash: transaction.PayloadHash,
Payload: payload,
Mass: mass,
}
dbResult := dbTx.Create(&dbTransaction)
dbErrors := dbResult.GetErrors()
@ -438,6 +455,85 @@ func insertTransaction(dbTx *gorm.DB, transaction *btcjson.TxRawResult, dbSubnet
return &dbTransaction, nil
}
func calcTxMass(dbTx *gorm.DB, transaction *btcjson.TxRawResult) (uint64, error) {
msgTx, err := convertTxRawResultToMsgTx(transaction)
if err != nil {
return 0, err
}
prevTxIDs := make([]string, len(transaction.Vin))
for i, txIn := range transaction.Vin {
prevTxIDs[i] = txIn.TxID
}
var prevDBTransactionsOutputs []dbmodels.TransactionOutput
dbResult := dbTx.
Joins("LEFT JOIN `transactions` ON `transactions`.`id` = `transaction_outputs`.`transaction_id`").
Where("transactions.transaction_id in (?)", prevTxIDs).
Preload("Transaction").
Find(&prevDBTransactionsOutputs)
dbErrors := dbResult.GetErrors()
if len(dbErrors) > 0 {
return 0, httpserverutils.NewErrorFromDBErrors("error fetching previous transactions: ", dbErrors)
}
prevScriptPubKeysMap := make(map[string]map[uint32][]byte)
for _, prevDBTransactionsOutput := range prevDBTransactionsOutputs {
txID := prevDBTransactionsOutput.Transaction.TransactionID
if prevScriptPubKeysMap[txID] == nil {
prevScriptPubKeysMap[txID] = make(map[uint32][]byte)
}
prevScriptPubKeysMap[txID][prevDBTransactionsOutput.Index] = prevDBTransactionsOutput.ScriptPubKey
}
orderedPrevScriptPubKeys := make([][]byte, len(transaction.Vin))
for i, txIn := range transaction.Vin {
orderedPrevScriptPubKeys[i] = prevScriptPubKeysMap[txIn.TxID][uint32(i)]
}
return blockdag.CalcTxMass(util.NewTx(msgTx), orderedPrevScriptPubKeys), nil
}
func convertTxRawResultToMsgTx(tx *btcjson.TxRawResult) (*wire.MsgTx, error) {
txIns := make([]*wire.TxIn, len(tx.Vin))
for i, txIn := range tx.Vin {
prevTxID, err := daghash.NewTxIDFromStr(txIn.TxID)
if err != nil {
return nil, err
}
signatureScript, err := hex.DecodeString(txIn.ScriptSig.Hex)
if err != nil {
return nil, err
}
txIns[i] = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *prevTxID,
Index: txIn.Vout,
},
SignatureScript: signatureScript,
Sequence: txIn.Sequence,
}
}
txOuts := make([]*wire.TxOut, len(tx.Vout))
for i, txOut := range tx.Vout {
scriptPubKey, err := hex.DecodeString(txOut.ScriptPubKey.Hex)
if err != nil {
return nil, err
}
txOuts[i] = &wire.TxOut{
Value: txOut.Value,
ScriptPubKey: scriptPubKey,
}
}
subnetworkID, err := subnetworkid.NewFromStr(tx.Subnetwork)
if err != nil {
return nil, err
}
if subnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) {
return wire.NewNativeMsgTx(tx.Version, txIns, txOuts), nil
}
payload, err := hex.DecodeString(tx.Payload)
if err != nil {
return nil, err
}
return wire.NewSubnetworkMsgTx(tx.Version, txIns, txOuts, subnetworkID, tx.Gas, payload), nil
}
func insertTransactionBlock(dbTx *gorm.DB, dbBlock *dbmodels.Block, dbTransaction *dbmodels.Transaction, index uint32) error {
var dbTransactionBlock dbmodels.TransactionBlock
dbResult := dbTx.

View File

@ -10,7 +10,7 @@ import (
const (
// maxBlocksInGetBlocksResult is the max amount of blocks that are
// allowed in a GetBlocksResult.
maxBlocksInGetBlocksResult = 100
maxBlocksInGetBlocksResult = 1000
)
func handleGetBlocks(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {