Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
Merge pull request #450 from BuxOrg/feat-291-change-broadcast-rc
Browse files Browse the repository at this point in the history
feat(BUX-291): adjust tx recording to BEEF requirements
  • Loading branch information
arkadiuszos4chain authored Nov 13, 2023
2 parents 9bbbedf + 989dd4c commit b59ead9
Show file tree
Hide file tree
Showing 28 changed files with 1,328 additions and 698 deletions.
125 changes: 28 additions & 97 deletions action_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,103 +24,15 @@ import (
// txHex is the raw transaction hex
// draftID is the unique draft id from a previously started New() transaction (draft_transaction.ID)
// opts are model options and can include "metadata"
func (c *Client) RecordTransaction(ctx context.Context, xPubKey, txHex, draftID string,
opts ...ModelOps,
) (*Transaction, error) {
// Check for existing NewRelic transaction
func (c *Client) RecordTransaction(ctx context.Context, xPubKey, txHex, draftID string, opts ...ModelOps) (*Transaction, error) {
ctx = c.GetOrStartTxn(ctx, "record_transaction")

// Create the model & set the default options (gives options from client->model)
newOpts := c.DefaultModelOptions(append(opts, WithXPub(xPubKey), New())...)
transaction := newTransactionWithDraftID(
txHex, draftID, newOpts...,
)

// Ensure that we have a transaction id (created from the txHex)
id := transaction.GetID()
if len(id) == 0 {
return nil, ErrMissingTxHex
}

var (
unlock func()
err error
)
// Create the lock and set the release for after the function completes
// Waits for the moment when the transaction is unlocked and creates a new lock
// Relevant for bux to bux transactions, as we have 1 tx but need to record 2 txs - outgoing and incoming
for {
unlock, err = newWriteLock(
ctx, fmt.Sprintf(lockKeyRecordTx, id), c.Cachestore(),
)
if err == nil {
break
}
time.Sleep(time.Second * 1)
}
defer unlock()

// OPTION: check incoming transactions (if enabled, will add to queue for checking on-chain)
if !c.IsITCEnabled() {
transaction.DebugLog("incoming transaction check is disabled")
} else {

// Incoming (external/unknown) transaction (no draft id was given)
if len(transaction.DraftID) == 0 {

// Process & save the model
incomingTx := newIncomingTransaction(
transaction.ID, txHex, newOpts...,
)
if err = incomingTx.Save(ctx); err != nil {
return nil, err
}

// Check if sync transaction exist. And if not, we should create it
if syncTx, _ := GetSyncTransactionByID(ctx, transaction.ID, transaction.client.DefaultModelOptions()...); syncTx == nil {
// Create the sync transaction model
sync := newSyncTransaction(
transaction.GetID(),
transaction.Client().DefaultSyncConfig(),
transaction.GetOptions(true)...,
)

// Skip broadcasting and skip P2P (incoming tx should have been broadcasted already)
sync.BroadcastStatus = SyncStatusSkipped // todo: this is an assumption
sync.P2PStatus = SyncStatusSkipped // The owner of the Tx should have already notified paymail providers

// Use the same metadata
sync.Metadata = transaction.Metadata

// If all the options are skipped, do not make a new model (ignore the record)
if !sync.isSkipped() {
if err = sync.Save(ctx); err != nil {
return nil, err
}
}
}
// Added to queue
return newTransactionFromIncomingTransaction(incomingTx), nil
}

// Internal tx (must match draft tx)
if transaction.draftTransaction, err = getDraftTransactionID(
ctx, transaction.XPubID, transaction.DraftID,
transaction.GetOptions(false)...,
); err != nil {
return nil, err
} else if transaction.draftTransaction == nil {
return nil, ErrDraftNotFound
}
}

// Process & save the transaction model
if err = transaction.Save(ctx); err != nil {
rts, err := getRecordTxStrategy(ctx, c, xPubKey, txHex, draftID)
if err != nil {
return nil, err
}

// Return the response
return transaction, nil
return recordTransaction(ctx, c, rts, opts...)
}

// RecordRawTransaction will parse the transaction and save it into the Datastore directly, without any checks
Expand Down Expand Up @@ -198,11 +110,30 @@ func (c *Client) recordTxHex(ctx context.Context, txHex string, opts ...ModelOps
return nil, err
}

// run before create to see whether xpub_in_ids or xpub_out_ids is set
if err = transaction.BeforeCreating(ctx); err != nil {
// Logic moved from BeforeCreating hook - should be refactorized in next iteration

// If we are external and the user disabled incoming transaction checking, check outputs
if transaction.isExternal() && !transaction.Client().IsITCEnabled() {
// Check that the transaction has >= 1 known destination
if !transaction.TransactionBase.hasOneKnownDestination(ctx, transaction.Client(), transaction.GetOptions(false)...) {
return nil, ErrNoMatchingOutputs
}
}

// Process the UTXOs
if err = transaction.processUtxos(ctx); err != nil {
return nil, err
}

// Set the values from the inputs/outputs and draft tx
transaction.TotalValue, transaction.Fee = transaction.getValues()

// Add values
transaction.NumberOfInputs = uint32(len(transaction.TransactionBase.parsedTx.Inputs))
transaction.NumberOfOutputs = uint32(len(transaction.TransactionBase.parsedTx.Outputs))

// /Logic moved from BeforeCreating hook - should be refactorized in next iteration

monitor := c.options.chainstate.Monitor()

if monitor != nil {
Expand All @@ -213,7 +144,7 @@ func (c *Client) recordTxHex(ctx context.Context, txHex string, opts ...ModelOps
}
}

// Process & save the transaction model
// save the transaction model
if err = transaction.Save(ctx); err != nil {
return nil, err
}
Expand Down Expand Up @@ -418,7 +349,7 @@ func (c *Client) UpdateTransactionMetadata(ctx context.Context, xPubID, id strin
return nil, err
}

// Save the model
// Save the model // update existing record
if err = transaction.Save(ctx); err != nil {
return nil, err
}
Expand Down Expand Up @@ -550,7 +481,7 @@ func (c *Client) RevertTransaction(ctx context.Context, id string) error {
transaction.XpubOutputValue = XpubOutputValue{"reverted": 0}
transaction.DeletedAt.Valid = true
transaction.DeletedAt.Time = time.Now()
err = transaction.Save(ctx)
err = transaction.Save(ctx) // update existing record

return err
}
12 changes: 10 additions & 2 deletions action_transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,16 @@ func initRevertTransactionData(t *testing.T) (context.Context, ClientInterface,
require.NoError(t, err)
assert.NotEmpty(t, hex)

var transaction *Transaction
transaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...)
newOpts := client.DefaultModelOptions(WithXPub(testXPub), New())
transaction := newTransactionWithDraftID(
hex, draftTransaction.ID, newOpts...,
)
transaction.draftTransaction = draftTransaction
_hydrateOutgoingWithSync(transaction)
err = transaction.processUtxos(ctx)
require.NoError(t, err)

err = transaction.Save(ctx)
require.NoError(t, err)
assert.NotEmpty(t, transaction)

Expand Down
2 changes: 1 addition & 1 deletion beef_tx_bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (beefTx *beefTx) toBeefBytes() ([]byte, error) {

// compose beef
buffer := make([]byte, 0, beefSize)
buffer = append(buffer, version...)
buffer = append(buffer, ver...)
buffer = append(buffer, nBUMPS...)
buffer = append(buffer, bumps...)

Expand Down
20 changes: 2 additions & 18 deletions beef_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,7 @@ import (
)

func Test_ToBeefHex(t *testing.T) {
t.Run("all parents txs are already mined", func(t *testing.T) {
// given
ctx, client, deferMe := initSimpleTestCase(t)
defer deferMe()

ancestorTx := addGrandpaTx(ctx, t, client)
minedParentTx := createTxWithDraft(ctx, t, client, ancestorTx, true)

newTx := createTxWithDraft(ctx, t, client, minedParentTx, false)

// when
hex, err := ToBeefHex(ctx, newTx)

// then
assert.NoError(t, err)
assert.NotEmpty(t, hex)
})
// TOOD: prepare tests in BUX-168

t.Run("some parents txs are not mined yet", func(t *testing.T) {
// Error expected! this should be changed in the future. right now the test case has been written to make sure the system doesn't panic in such a situation
Expand Down Expand Up @@ -64,7 +48,7 @@ func addGrandpaTx(ctx context.Context, t *testing.T, client ClientInterface) *Tr
},
}
grandpaTx.MerkleProof = MerkleProof(grandpaTxMp)
grandpaTx.BUMP = grandpaTx.MerkleProof.ToBUMP()
grandpaTx.BUMP = grandpaTx.MerkleProof.ToBUMP(grandpaTx.BlockHeight)
err := grandpaTx.Save(ctx)
require.NoError(t, err)

Expand Down
77 changes: 1 addition & 76 deletions db_model_transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,80 +59,6 @@ func (m *Transaction) BeforeCreating(ctx context.Context) error {
return err
}

// m.xPubID is the xpub of the user registering the transaction
if len(m.XPubID) > 0 && len(m.DraftID) > 0 {
// Only get the draft if we haven't already
if m.draftTransaction == nil {
if m.draftTransaction, err = getDraftTransactionID(
ctx, m.XPubID, m.DraftID, m.GetOptions(false)...,
); err != nil {
return err
} else if m.draftTransaction == nil {
return ErrDraftNotFound
}
}
}

// Validations and broadcast config check
if m.draftTransaction != nil {

// No config set? Use the default from the client
if m.draftTransaction.Configuration.Sync == nil {
m.draftTransaction.Configuration.Sync = m.Client().DefaultSyncConfig()
}

// Create the sync transaction model
sync := newSyncTransaction(
m.GetID(),
m.draftTransaction.Configuration.Sync,
m.GetOptions(true)...,
)

// Found any p2p outputs?
p2pStatus := SyncStatusSkipped
if m.draftTransaction.Configuration.Outputs != nil {
for _, output := range m.draftTransaction.Configuration.Outputs {
if output.PaymailP4 != nil && output.PaymailP4.ResolutionType == ResolutionTypeP2P {
p2pStatus = SyncStatusPending
}
}
}
sync.P2PStatus = p2pStatus

// Use the same metadata
sync.Metadata = m.Metadata

// set this transaction on the sync transaction object. This is needed for the first broadcast
sync.transaction = m

// If all the options are skipped, do not make a new model (ignore the record)
if !sync.isSkipped() {
m.syncTransaction = sync
}
}

// If we are external and the user disabled incoming transaction checking, check outputs
if m.isExternal() && !m.Client().IsITCEnabled() {
// Check that the transaction has >= 1 known destination
if !m.TransactionBase.hasOneKnownDestination(ctx, m.Client(), m.GetOptions(false)...) {
return ErrNoMatchingOutputs
}
}

// Process the UTXOs
if err = m.processUtxos(ctx); err != nil {
return err
}

// Set the values from the inputs/outputs and draft tx
m.TotalValue, m.Fee = m.getValues()

// Add values if found
if m.TransactionBase.parsedTx != nil {
m.NumberOfInputs = uint32(len(m.TransactionBase.parsedTx.Inputs))
m.NumberOfOutputs = uint32(len(m.TransactionBase.parsedTx.Outputs))
}

m.DebugLog("end: " + m.Name() + " BeforeCreating hook")
m.beforeCreateCalled = true
return nil
Expand Down Expand Up @@ -297,8 +223,7 @@ func (m *Transaction) migrateBUMP() error {
return err
}
for _, tx := range txs {
bump := tx.MerkleProof.ToBUMP()
bump.BlockHeight = tx.BlockHeight
bump := tx.MerkleProof.ToBUMP(tx.BlockHeight)
tx.BUMP = bump
_ = tx.Save(ctx)
}
Expand Down
11 changes: 11 additions & 0 deletions mock_chainstate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/bitcoin-sv/go-broadcast-client/broadcast"
"github.com/libsv/go-bc"
"github.com/tonicpow/go-minercraft/v2"

"github.com/BuxOrg/bux/chainstate"
Expand Down Expand Up @@ -133,6 +134,11 @@ func (c *chainStateEverythingOnChain) QueryTransaction(_ context.Context, id str
ID: id,
MinerID: "",
Provider: "whatsonchain",
MerkleProof: &bc.MerkleProof{
Index: 37008,
TxOrID: id,
Nodes: []string{"3228f78cfd3c96262ec521225f1b9dd6326b4d3e245d1551bb06258f2101cb65", "05267706279d2e5ebcf89ed0645d4283108c7e850cdb84aeb0974738ae447a8d"},
},
}, nil
}

Expand All @@ -147,6 +153,11 @@ func (c *chainStateEverythingOnChain) QueryTransactionFastest(_ context.Context,
ID: id,
MinerID: "",
Provider: "whatsonchain",
MerkleProof: &bc.MerkleProof{
Index: 37008,
TxOrID: id,
Nodes: []string{"3228f78cfd3c96262ec521225f1b9dd6326b4d3e245d1551bb06258f2101cb65", "05267706279d2e5ebcf89ed0645d4283108c7e850cdb84aeb0974738ae447a8d"},
},
}, nil
}

Expand Down
Loading

0 comments on commit b59ead9

Please sign in to comment.