Skip to content

Commit

Permalink
Specify vout index for coin identifier (#11)
Browse files Browse the repository at this point in the history
* Specify vout index for coin identifier

* remove .idea files

* fix
  • Loading branch information
yurikoinaba committed Oct 12, 2021
1 parent df269fb commit 24af9f9
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 42 deletions.
122 changes: 84 additions & 38 deletions zen/client.go
Expand Up @@ -221,24 +221,36 @@ func (b *Client) GetRawBlock(
coins := []string{}
blockTxHashes := []string{}
for txIndex, tx := range block.Txs {
blockTxHashes = append(blockTxHashes, tx.Hash)
for inputIndex, input := range tx.Inputs {
txHash, vout, ok := b.getInputTxHash(input, txIndex, inputIndex)
if !ok {
continue
}
coins, blockTxHashes = addCoins(txIndex, blockTxHashes, tx.Hash, tx.Inputs, b, coins)
}

// If any transactions spent in the same block they are created, don't include them
// in previousTxHashes to fetch.
if !utils.ContainsString(blockTxHashes, txHash) {
coins = append(coins, CoinIdentifier(txHash, vout))
}
}
blockCertTxHashes := []string{}
for certTxIndex, certTx := range block.Certs {
coins, blockCertTxHashes = addCoins(certTxIndex, blockCertTxHashes, certTx.Hash, certTx.Inputs, b, coins)
}

return block, coins, nil
}


func addCoins(txIndex int, blockTxHashes []string, hash string, inputs []*Input, b *Client, coins []string) ([]string, []string) {
blockTxHashes = append(blockTxHashes, hash)
for inputIndex, input := range inputs {
txHash, vout, ok := b.getInputTxHash(input, txIndex, inputIndex)
if !ok {
continue
}

// If any transactions spent in the same block they are created, don't include them
// in previousTxHashes to fetch.
if !utils.ContainsString(blockTxHashes, txHash) {
coins = append(coins, CoinIdentifier(txHash, vout))
}
}

return coins, blockTxHashes
}

// ParseBlock returns a parsed bitcoin block given a raw bitcoin
// block and a map of transactions containing inputs.
func (b *Client) ParseBlock(
Expand Down Expand Up @@ -493,9 +505,9 @@ func (b *Client) parseTransactions(
if block == nil {
return nil, errors.New("error parsing nil block")
}
txs := make([]*types.Transaction, len(block.Txs))
txs := make([]*types.Transaction, len(block.Txs) + len(block.Certs))
for index, transaction := range block.Txs {
txOps, err := b.parseTxOperations(transaction, index, coins)
txOps, err := b.parseTxOperations(transaction.Inputs, transaction.Outputs, transaction.Hash, index, coins, false)
if err != nil {
return nil, fmt.Errorf("%w: error parsing transaction operations", err)
}
Expand All @@ -515,41 +527,68 @@ func (b *Client) parseTransactions(

txs[index] = tx

// In some cases, a transaction will spent an output
// from the same block.
for _, op := range tx.Operations {
if op.CoinChange == nil {
continue
}
coins = addCoinsFromSameBlock(tx.Operations, coins)
}

if op.CoinChange.CoinAction != types.CoinCreated {
continue
}
for index, certificate := range block.Certs {
certTxOps, err := b.parseTxOperations(certificate.Inputs, certificate.Outputs, certificate.Hash, index, coins, true)
if err != nil {
return nil, fmt.Errorf("%w: error parsing certificate transaction operations", err)
}

coins[op.CoinChange.CoinIdentifier.Identifier] = &storage.AccountCoin{
Coin: &types.Coin{
CoinIdentifier: op.CoinChange.CoinIdentifier,
Amount: op.Amount,
},
Account: op.Account,
}
tx := &types.Transaction{
TransactionIdentifier: &types.TransactionIdentifier{
Hash: certificate.Hash,
},
Operations: certTxOps,
}

txs[len(block.Txs) + index] = tx

coins = addCoinsFromSameBlock(tx.Operations, coins)
}

return txs, nil

}

func addCoinsFromSameBlock(operations []*types.Operation, coins map[string]*storage.AccountCoin) map[string]*storage.AccountCoin {
// In some cases, a transaction will spent an output
// from the same block.
for _, op := range operations {
if op.CoinChange == nil {
continue
}

if op.CoinChange.CoinAction != types.CoinCreated {
continue
}

coins[op.CoinChange.CoinIdentifier.Identifier] = &storage.AccountCoin{
Coin: &types.Coin{
CoinIdentifier: op.CoinChange.CoinIdentifier,
Amount: op.Amount,
},
Account: op.Account,
}
}

return coins
}

// parseTransactions returns the transaction operations for a specified transaction.
// It uses a map of previous transactions to properly hydrate the input operations.
func (b *Client) parseTxOperations(
tx *Transaction,
inputs []*Input,
outputs []*Output,
hash string,
txIndex int,
coins map[string]*storage.AccountCoin,
isCertificate bool,
) ([]*types.Operation, error) {
txOps := []*types.Operation{}

for networkIndex, input := range tx.Inputs {
for networkIndex, input := range inputs {
if bitcoinIsCoinbaseInput(input, txIndex, networkIndex) {
txOp, err := b.coinbaseTxOperation(input, int64(len(txOps)), int64(networkIndex))
if err != nil {
Expand All @@ -566,7 +605,7 @@ func (b *Client) parseTxOperations(
return nil, fmt.Errorf(
"error finding previous tx: %s, for tx: %s, input index: %d",
input.TxHash,
tx.Hash,
hash,
networkIndex,
)
}
Expand All @@ -585,20 +624,27 @@ func (b *Client) parseTxOperations(
txOps = append(txOps, txOp)
}

for networkIndex, output := range tx.Outputs {
for _, output := range outputs {

if isCertificate == true && output.BackwardTransfer == true {
continue
}

outputIndex := int64(output.Index)

txOp, err := b.parseOutputTransactionOperation(
output,
tx.Hash,
hash,
int64(len(txOps)),
int64(networkIndex),
outputIndex,
txIndex,
)
if err != nil {
return nil, fmt.Errorf(
"%w: error parsing tx output, hash: %s, index: %d",
err,
tx.Hash,
networkIndex,
hash,
outputIndex,
)
}

Expand Down
32 changes: 28 additions & 4 deletions zen/types.go
Expand Up @@ -197,7 +197,8 @@ type Block struct {
Bits string `json:"bits"`
Difficulty float64 `json:"difficulty"`

Txs []*Transaction `json:"tx"`
Txs []*Transaction `json:"tx"`
Certs []*Certificate `json:"cert"`
}

// Metadata returns the metadata for a block.
Expand Down Expand Up @@ -239,6 +240,28 @@ type Transaction struct {
Joinsplits []*Joinsplit `json:"vjoinsplit"`
}

type Certificate struct {
Hash string `json:"txid"`
Version int32 `json:"version"`
Inputs []*Input `json:"vin"`
Cert *Cert `json:"cert"`
Outputs []*Output `json:"vout"`
Joinsplits []*Joinsplit `json:"vjoinsplit"`
}

type Cert struct {
Scid string `json:"scid"`
EpochNumber int64 `json:"epochNumber"`
Quality int64 `json:"quality"`
EndEpochCumScTxCommTreeRoot string `json:"endEpochCumScTxCommTreeRoot"`
ScProof string `json:"scProof"`
VFieldElementCertificateField []string `json:"vFieldElementCertificateField"`
VBitVectorCertificateField []string `json:"vBitVectorCertificateField"`
FtScFee float64 `json:"ftScFee"`
MbtrScFee float64 `json:"mbtrScFee"`
TotalAmount float64 `json:"totalAmount"`
}

// Joinsplit is a raw Joinsplit transaction representation.
type Joinsplit struct {
VPubOld float64 `json:"vpub_old"`
Expand Down Expand Up @@ -298,9 +321,10 @@ func (i Input) Metadata() (map[string]interface{}, error) {

// Output is a raw output in a Bitcoin transaction.
type Output struct {
Value float64 `json:"value"`
Index int64 `json:"n"`
ScriptPubKey *ScriptPubKey `json:"scriptPubKey"`
Value float64 `json:"value"`
Index int64 `json:"n"`
ScriptPubKey *ScriptPubKey `json:"scriptPubKey"`
BackwardTransfer bool `json:"backwardTransfer,omitempty"`
}

// Metadata returns the metadata for an output.
Expand Down

0 comments on commit 24af9f9

Please sign in to comment.