diff --git a/README.md b/README.md index 0b071fd..8891af6 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Events are created with a simple schema. { "type": "event type", "timestamp": "wall clock timestamp of event", + "context": "metadata about the event", "payload": "the full event specific payload" } ``` @@ -28,10 +29,14 @@ The chainsync input produces three event types: `block`, `rollback`, and block: ```json { - "payload": { + "context": { "blockNumber": 123, - "blockHash": "abcd123...", "slotNumber": 1234567, + }, + "payload": { + "blockBodySize": 123, + "issuerVkey": "a712f81ab2eac...", + "blockHash": "abcd123...", "blockCbor": "85828a1a000995c21..." } } @@ -50,12 +55,43 @@ rollback: transaction: ```json { - "payload": { + "context": { "blockNumber": 123, - "blockHash": "abcd123...", "slotNumber": 1234567, "transactionHash": "0deadbeef123...", + "transactionIdx": 0, + }, + "payload": { + "blockHash": "abcd123...", "transactionCbor": "a500828258200a1ad..." + "inputs": [ + "abcdef123...#0", + "abcdef123...#1", + ], + "outputs": [ + { + "address": "addr1qwerty123...", + "amount": 12345687, + "assets": [ + { + "name": "Foo", + "nameHex": "abcd123...", + "amount": 123, + "fingerprint": "asset1abcd...", + "policyId": "54321..." + } + ] + } + ], + "metadata": { + 674: { + "msg": [ + "Test message" + ] + } + }, + "fee": 1234567, + "ttl": 123 } } ``` diff --git a/input/chainsync/chainsync.go b/input/chainsync/chainsync.go index 9d7e1a4..bd29426 100644 --- a/input/chainsync/chainsync.go +++ b/input/chainsync/chainsync.go @@ -220,8 +220,8 @@ func (c *ChainSync) handleRollForward(blockType uint, blockData interface{}, tip } blockEvt := event.New("chainsync.block", time.Now(), NewBlockHeaderContext(v), NewBlockEvent(block, c.includeCbor)) c.eventChan <- blockEvt - for _, transaction := range block.Transactions() { - txEvt := event.New("chainsync.transaction", time.Now(), nil, NewTransactionEvent(block, transaction, c.includeCbor)) + for t, transaction := range block.Transactions() { + txEvt := event.New("chainsync.transaction", time.Now(), NewTransactionContext(block, transaction, uint32(t)), NewTransactionEvent(block, transaction, c.includeCbor)) c.eventChan <- txEvt } c.updateStatus(v.SlotNumber(), v.BlockNumber(), v.Hash(), tip.Point.Slot, hex.EncodeToString(tip.Point.Hash)) @@ -230,10 +230,10 @@ func (c *ChainSync) handleRollForward(blockType uint, blockData interface{}, tip } func (c *ChainSync) handleBlockFetchBlock(block ledger.Block) error { - blockEvt := event.New("chainsync.block", time.Now(), nil, NewBlockEvent(block, c.includeCbor)) + blockEvt := event.New("chainsync.block", time.Now(), NewBlockContext(block), NewBlockEvent(block, c.includeCbor)) c.eventChan <- blockEvt - for _, transaction := range block.Transactions() { - txEvt := event.New("chainsync.transaction", time.Now(), nil, NewTransactionEvent(block, transaction, c.includeCbor)) + for t, transaction := range block.Transactions() { + txEvt := event.New("chainsync.transaction", time.Now(), NewTransactionContext(block, transaction, uint32(t)), NewTransactionEvent(block, transaction, c.includeCbor)) c.eventChan <- txEvt } c.updateStatus(block.SlotNumber(), block.BlockNumber(), block.Hash(), c.bulkRangeEnd.Slot, hex.EncodeToString(c.bulkRangeEnd.Hash)) diff --git a/input/chainsync/tx.go b/input/chainsync/tx.go index e37ae1e..d530fda 100644 --- a/input/chainsync/tx.go +++ b/input/chainsync/tx.go @@ -19,25 +19,40 @@ import ( "github.com/blinklabs-io/gouroboros/ledger" ) +type TransactionContext struct { + BlockNumber uint64 `json:"blockNumber"` + SlotNumber uint64 `json:"slotNumber"` + TransactionHash string `json:"transactionHash"` + TransactionIdx uint32 `json:"transactionIdx"` +} + type TransactionEvent struct { - BlockNumber uint64 `json:"blockNumber"` BlockHash string `json:"blockHash"` - SlotNumber uint64 `json:"slotNumber"` - TransactionHash string `json:"transactionHash"` TransactionCbor byteSliceJsonHex `json:"transactionCbor,omitempty"` Inputs []ledger.TransactionInput `json:"inputs"` Outputs []ledger.TransactionOutput `json:"outputs"` Metadata *cbor.Value `json:"metadata,omitempty"` + Fee uint64 `json:"fee"` + TTL uint64 `json:"ttl,omitempty"` } -func NewTransactionEvent(block ledger.Block, tx ledger.Transaction, includeCbor bool) TransactionEvent { - evt := TransactionEvent{ +func NewTransactionContext(block ledger.Block, tx ledger.Transaction, index uint32) TransactionContext { + ctx := TransactionContext{ BlockNumber: block.BlockNumber(), - BlockHash: block.Hash(), SlotNumber: block.SlotNumber(), TransactionHash: tx.Hash(), + TransactionIdx: index, + } + return ctx +} + +func NewTransactionEvent(block ledger.Block, tx ledger.Transaction, includeCbor bool) TransactionEvent { + evt := TransactionEvent{ + BlockHash: block.Hash(), Inputs: tx.Inputs(), Outputs: tx.Outputs(), + Fee: tx.Fee(), + TTL: tx.TTL(), } if includeCbor { evt.TransactionCbor = tx.Cbor() diff --git a/output/notify/notify.go b/output/notify/notify.go index 2f7e793..fdb5e3c 100644 --- a/output/notify/notify.go +++ b/output/notify/notify.go @@ -97,16 +97,22 @@ func (n *NotifyOutput) Start() error { if payload == nil { panic(fmt.Errorf("ERROR: %v", payload)) } + context := evt.Context + if context == nil { + panic(fmt.Errorf("ERROR: %v", context)) + } te := payload.(chainsync.TransactionEvent) + tc := context.(chainsync.TransactionContext) err := beeep.Notify( n.title, - fmt.Sprintf("New Transaction!\nBlockNumber: %d, SlotNumber: %d\nInputs: %d, Outputs: %d\nHash: %s", - te.BlockNumber, - te.SlotNumber, + fmt.Sprintf("New Transaction!\nBlockNumber: %d, SlotNumber: %d\nInputs: %d, Outputs: %d\nFee: %d\nHash: %s", + tc.BlockNumber, + tc.SlotNumber, len(te.Inputs), len(te.Outputs), - te.TransactionHash, + te.Fee, + tc.TransactionHash, ), "assets/snek-icon.png", ) diff --git a/output/push/push.go b/output/push/push.go index 9719b49..67ed6ca 100644 --- a/output/push/push.go +++ b/output/push/push.go @@ -114,17 +114,23 @@ func (p *PushOutput) Start() error { if payload == nil { panic(fmt.Errorf("ERROR: %v", payload)) } + context := evt.Context + if context == nil { + panic(fmt.Errorf("ERROR: %v", context)) + } te := payload.(chainsync.TransactionEvent) + tc := context.(chainsync.TransactionContext) // Create notification message title := "Snek" - body := fmt.Sprintf("New Transaction!\nBlockNumber: %d, SlotNumber: %d\nInputs: %d, Outputs: %d\nHash: %s", - te.BlockNumber, - te.SlotNumber, + body := fmt.Sprintf("New Transaction!\nBlockNumber: %d, SlotNumber: %d\nInputs: %d, Outputs: %d\nFee: %d\nHash: %s", + tc.BlockNumber, + tc.SlotNumber, len(te.Inputs), len(te.Outputs), - te.TransactionHash, + te.Fee, + tc.TransactionHash, ) // Send notification diff --git a/output/webhook/webhook.go b/output/webhook/webhook.go index 9d8ab90..1570a74 100644 --- a/output/webhook/webhook.go +++ b/output/webhook/webhook.go @@ -155,14 +155,15 @@ func formatWebhook(e *event.Event, format string) []byte { }) case "chainsync.transaction": te := e.Payload.(chainsync.TransactionEvent) + tc := e.Context.(chainsync.TransactionContext) dme.Title = "New Cardano Transaction" dmefs = append(dmefs, &DiscordMessageEmbedField{ Name: "Block Number", - Value: fmt.Sprintf("%d", te.BlockNumber), + Value: fmt.Sprintf("%d", tc.BlockNumber), }) dmefs = append(dmefs, &DiscordMessageEmbedField{ Name: "Slot Number", - Value: fmt.Sprintf("%d", te.SlotNumber), + Value: fmt.Sprintf("%d", tc.SlotNumber), }) dmefs = append(dmefs, &DiscordMessageEmbedField{ Name: "Inputs", @@ -172,12 +173,16 @@ func formatWebhook(e *event.Event, format string) []byte { Name: "Outputs", Value: fmt.Sprintf("%d", len(te.Outputs)), }) + dmefs = append(dmefs, &DiscordMessageEmbedField{ + Name: "Fee", + Value: fmt.Sprintf("%d", te.Fee), + }) dmefs = append(dmefs, &DiscordMessageEmbedField{ Name: "Transaction Hash", - Value: te.TransactionHash, + Value: tc.TransactionHash, }) // TODO: fix this URL for different networks - dme.URL = fmt.Sprintf("https://cexplorer.io/tx/%s", te.TransactionHash) + dme.URL = fmt.Sprintf("https://cexplorer.io/tx/%s", tc.TransactionHash) default: dwe.Content = fmt.Sprintf("%v", e.Payload) }