TxOutputs: extract and index pkscript metadata (script type, addresses, #required sigs); TxInputs: outpoint_hash => outpoint_tx_id that references transaction_cids.id

This commit is contained in:
Ian Norden 2020-02-04 15:20:49 -06:00
parent 48f70d4ddf
commit f33cc3f34b
10 changed files with 102 additions and 58 deletions

View File

@ -3,11 +3,10 @@ CREATE TABLE btc.transaction_cids (
id SERIAL PRIMARY KEY,
header_id INTEGER NOT NULL REFERENCES btc.header_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
index INTEGER NOT NULL,
tx_hash VARCHAR(66) NOT NULL,
tx_hash VARCHAR(66) NOT NULL UNIQUE,
cid TEXT NOT NULL,
has_witness BOOL NOT NULL,
witness_hash VARCHAR(66),
UNIQUE (header_id, tx_hash)
segwit BOOL NOT NULL,
witness_hash VARCHAR(66)
);
-- +goose Down

View File

@ -3,9 +3,9 @@ CREATE TABLE btc.tx_inputs (
id SERIAL PRIMARY KEY,
tx_id INTEGER NOT NULL REFERENCES btc.transaction_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
index INTEGER NOT NULL,
tx_witness BYTEA[],
witness BYTEA[],
sig_script BYTEA NOT NULL,
outpoint_hash VARCHAR(66) NOT NULL,
outpoint_tx_id INTEGER NOT NULL REFERENCES btc.transaction_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
outpoint_index INTEGER NOT NULL,
UNIQUE (tx_id, index)
);

View File

@ -5,6 +5,9 @@ CREATE TABLE btc.tx_outputs (
index INTEGER NOT NULL,
value INTEGER NOT NULL,
pk_script BYTEA NOT NULL,
script_class INTEGER NOT NULL,
addresses VARCHAR(66)[],
required_sigs INTEGER NOT NULL,
UNIQUE (tx_id, index)
);

View File

@ -78,7 +78,7 @@ CREATE TABLE btc.transaction_cids (
index integer NOT NULL,
tx_hash character varying(66) NOT NULL,
cid text NOT NULL,
has_witness boolean NOT NULL,
segwit boolean NOT NULL,
witness_hash character varying(66)
);
@ -111,10 +111,9 @@ CREATE TABLE btc.tx_inputs (
id integer NOT NULL,
tx_id integer NOT NULL,
index integer NOT NULL,
tx_witness bytea[],
sequence integer NOT NULL,
script bytea NOT NULL,
outpoint_hash character varying(66) NOT NULL,
witness bytea[],
sig_script bytea NOT NULL,
outpoint_tx_id integer NOT NULL,
outpoint_index integer NOT NULL
);
@ -148,7 +147,10 @@ CREATE TABLE btc.tx_outputs (
tx_id integer NOT NULL,
index integer NOT NULL,
value integer NOT NULL,
script bytea NOT NULL
pk_script bytea NOT NULL,
script_class integer NOT NULL,
addresses character varying(66)[],
required_sigs integer NOT NULL
);
@ -1275,14 +1277,6 @@ ALTER TABLE ONLY btc.header_cids
ADD CONSTRAINT header_cids_pkey PRIMARY KEY (id);
--
-- Name: transaction_cids transaction_cids_header_id_tx_hash_key; Type: CONSTRAINT; Schema: btc; Owner: -
--
ALTER TABLE ONLY btc.transaction_cids
ADD CONSTRAINT transaction_cids_header_id_tx_hash_key UNIQUE (header_id, tx_hash);
--
-- Name: transaction_cids transaction_cids_pkey; Type: CONSTRAINT; Schema: btc; Owner: -
--
@ -1291,6 +1285,14 @@ ALTER TABLE ONLY btc.transaction_cids
ADD CONSTRAINT transaction_cids_pkey PRIMARY KEY (id);
--
-- Name: transaction_cids transaction_cids_tx_hash_key; Type: CONSTRAINT; Schema: btc; Owner: -
--
ALTER TABLE ONLY btc.transaction_cids
ADD CONSTRAINT transaction_cids_tx_hash_key UNIQUE (tx_hash);
--
-- Name: tx_inputs tx_inputs_pkey; Type: CONSTRAINT; Schema: btc; Owner: -
--
@ -1744,6 +1746,14 @@ ALTER TABLE ONLY btc.transaction_cids
ADD CONSTRAINT transaction_cids_header_id_fkey FOREIGN KEY (header_id) REFERENCES btc.header_cids(id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
--
-- Name: tx_inputs tx_inputs_outpoint_tx_id_fkey; Type: FK CONSTRAINT; Schema: btc; Owner: -
--
ALTER TABLE ONLY btc.tx_inputs
ADD CONSTRAINT tx_inputs_outpoint_tx_id_fkey FOREIGN KEY (outpoint_tx_id) REFERENCES btc.transaction_cids(id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
--
-- Name: tx_inputs tx_inputs_tx_id_fkey; Type: FK CONSTRAINT; Schema: btc; Owner: -
--

View File

@ -19,14 +19,19 @@ package btc
import (
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/vulcanize/vulcanizedb/pkg/super_node/shared"
)
// PayloadConverter satisfies the PayloadConverter interface for bitcoin
type PayloadConverter struct{}
type PayloadConverter struct{
chainConfig *chaincfg.Params
}
// NewPayloadConverter creates a pointer to a new PayloadConverter which satisfies the PayloadConverter interface
func NewPayloadConverter() *PayloadConverter {
func NewPayloadConverter(chainConfig *chaincfg.Params) *PayloadConverter {
return &PayloadConverter{}
}
@ -42,8 +47,8 @@ func (pc *PayloadConverter) Convert(payload shared.RawChainData) (shared.Streame
index := tx.Index()
txModel := TxModelWithInsAndOuts{
TxHash: tx.Hash().String(),
Index: int64(tx.Index()),
HasWitness: tx.HasWitness(),
Index: int64(index),
SegWit: tx.HasWitness(),
TxOutputs: make([]TxOutput, len(tx.MsgTx().TxOut)),
TxInputs: make([]TxInput, len(tx.MsgTx().TxIn)),
}
@ -60,10 +65,22 @@ func (pc *PayloadConverter) Convert(payload shared.RawChainData) (shared.Streame
}
}
for i, out := range tx.MsgTx().TxOut {
scriptClass, addresses, numberOfSigs, err := txscript.ExtractPkScriptAddrs(out.PkScript, pc.chainConfig)
// if we receive an error but the txscript type isn't NonStandardTy then something went wrong
if err != nil && scriptClass != txscript.NonStandardTy {
return nil, err
}
stringAddrs := make([]string, len(addresses))
for i, addr := range addresses {
stringAddrs[i] = addr.EncodeAddress()
}
txModel.TxOutputs[i] = TxOutput{
Index: int64(i),
Value: out.Value,
PkScript: out.PkScript,
RequiredSigs: int64(numberOfSigs),
ScriptClass: (uint8)(scriptClass),
Addresses: stringAddrs,
}
}
txMeta[index] = txModel

View File

@ -87,26 +87,34 @@ func (in *CIDIndexer) indexTransactionCIDs(tx *sqlx.Tx, transactions []TxModelWi
func (in *CIDIndexer) indexTransactionCID(tx *sqlx.Tx, transaction TxModelWithInsAndOuts, headerID int64) (int64, error) {
var txID int64
err := tx.QueryRowx(`INSERT INTO btc.transaction_cids (header_id, tx_hash, index, cid, has_witness, witness_hash)
err := tx.QueryRowx(`INSERT INTO btc.transaction_cids (header_id, tx_hash, index, cid, segwit, witness_hash)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (header_id, tx_hash) DO UPDATE SET (index, cid, has_witness, witness_hash) = ($3, $4, $5, $6)
ON CONFLICT (tx_hash) DO UPDATE SET (header_id, index, cid, segwit, witness_hash) = ($1, $3, $4, $5, $6)
RETURNING id`,
headerID, transaction.TxHash, transaction.Index, transaction.CID, transaction.HasWitness, transaction.WitnessHash).Scan(&txID)
headerID, transaction.TxHash, transaction.Index, transaction.CID, transaction.SegWit, transaction.WitnessHash).Scan(&txID)
return txID, err
}
func (in *CIDIndexer) indexTxInput(tx *sqlx.Tx, txInput TxInput, txID int64) error {
_, err := tx.Exec(`INSERT INTO btc.tx_inputs (tx_id, index, tx_witness, sig_script, outpoint_hash, outpoint_index)
var referencedOutPutTxID int64
if err := tx.Get(&referencedOutPutTxID, `SELECT id FROM btc.transaction_cids
WHERE tx_hash = $1`, txInput.PreviousOutPointHash); err != nil {
return err
}
if referencedOutPutTxID == 0 {
return fmt.Errorf("btc indexer could not find the tx hash %s referenced in tx input of tx id %d", txInput.PreviousOutPointHash, txID)
}
_, err := tx.Exec(`INSERT INTO btc.tx_inputs (tx_id, index, witness, sig_script, outpoint_tx_id, outpoint_index)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (tx_id, index) DO UPDATE SET (tx_witness, sig_script, outpoint_hash, outpoint_index) = ($3, $4, $5, $6)`,
txID, txInput.Index, pq.Array(txInput.TxWitness), txInput.SignatureScript, txInput.PreviousOutPointHash, txInput.PreviousOutPointIndex)
ON CONFLICT (tx_id, index) DO UPDATE SET (witness, sig_script, outpoint_tx_id, outpoint_index) = ($3, $4, $5, $6)`,
txID, txInput.Index, pq.Array(txInput.TxWitness), txInput.SignatureScript, referencedOutPutTxID, txInput.PreviousOutPointIndex)
return err
}
func (in *CIDIndexer) indexTxOutput(tx *sqlx.Tx, txOuput TxOutput, txID int64) error {
_, err := tx.Exec(`INSERT INTO btc.tx_outputs (tx_id, index, value, pk_script)
VALUES ($1, $2, $3, $4)
ON CONFLICT (ix_id, index) DO UPDATE SET (value, pk_script) = ($3, $4)`,
txID, txOuput.Index, txOuput.Value, txOuput.PkScript)
_, err := tx.Exec(`INSERT INTO btc.tx_outputs (tx_id, index, value, pk_script, script_class, addresses, required_sigs)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (ix_id, index) DO UPDATE SET (value, pk_script, script_class, addresses, required_sigs) = ($3, $4, $5, $6, $7)`,
txID, txOuput.Index, txOuput.Value, txOuput.PkScript, txOuput.ScriptClass, txOuput.Addresses, txOuput.RequiredSigs)
return err
}

View File

@ -16,6 +16,8 @@
package btc
import "github.com/lib/pq"
// HeaderModel is the db model for btc.header_cids table
type HeaderModel struct {
ID int64 `db:"id"`
@ -35,7 +37,7 @@ type TxModel struct {
Index int64 `db:"index"`
TxHash string `db:"tx_hash"`
CID string `db:"cid"`
HasWitness bool `db:"has_witness"`
SegWit bool `db:"segwit"`
WitnessHash string `db:"witness_hash"`
}
@ -46,7 +48,7 @@ type TxModelWithInsAndOuts struct {
Index int64 `db:"index"`
TxHash string `db:"tx_hash"`
CID string `db:"cid"`
HasWitness bool `db:"has_witness"`
SegWit bool `db:"segwit"`
WitnessHash string `db:"witness_hash"`
TxInputs []TxInput
TxOutputs []TxOutput
@ -57,10 +59,11 @@ type TxInput struct {
ID int64 `db:"id"`
TxID int64 `db:"tx_id"`
Index int64 `db:"index"`
TxWitness [][]byte `db:"tx_witness"`
TxWitness [][]byte `db:"witness"`
SignatureScript []byte `db:"sig_script"`
PreviousOutPointHash string `db:"outpoint_hash"`
PreviousOutPointTxID int64 `db:"outpoint_tx_id"`
PreviousOutPointIndex uint32 `db:"outpoint_index"`
PreviousOutPointHash string
}
// TxOutput is the db model for btc.tx_outputs table
@ -70,4 +73,7 @@ type TxOutput struct {
Index int64 `db:"index"`
Value int64 `db:"value"`
PkScript []byte `db:"pk_script"`
ScriptClass uint8 `db:"script_class"`
RequiredSigs int64 `db:"required_sigs"`
Addresses pq.StringArray `db:"addresses"`
}

View File

@ -101,7 +101,7 @@ func (pub *IPLDPublisher) publishTransactions(transactions []*btcutil.Tx, trxMet
CID: cid,
Index: trxMeta[i].Index,
TxHash: trxMeta[i].TxHash,
HasWitness: trxMeta[i].HasWitness,
SegWit: trxMeta[i].SegWit,
WitnessHash: trxMeta[i].WitnessHash,
TxInputs: trxMeta[i].TxInputs,
TxOutputs: trxMeta[i].TxOutputs,

View File

@ -77,8 +77,8 @@ type IPLDWrapper struct {
// Passed to client subscriptions
type StreamPayload struct {
BlockNumber *big.Int `json:"blockNumber"`
HeadersBytes [][]byte `json:"headerBytes"`
TransactionsBytes [][]byte `json:"transactionBytes"`
SerializedHeaders [][]byte `json:"headerBytes"`
SerializedTxs [][]byte `json:"transactionBytes"`
encoded []byte
err error

View File

@ -18,6 +18,7 @@ package super_node
import (
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/rpcclient"
"github.com/ethereum/go-ethereum/params"
@ -106,7 +107,7 @@ func NewPayloadConverter(chain shared.ChainType) (shared.PayloadConverter, error
case shared.Ethereum:
return eth.NewPayloadConverter(params.MainnetChainConfig), nil
case shared.Bitcoin:
return btc.NewPayloadConverter(), nil
return btc.NewPayloadConverter(&chaincfg.MainNetParams), nil
default:
return nil, fmt.Errorf("invalid chain %T for converter constructor", chain)
}