Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,32 @@ variable. For example, the `-input` option has the `INPUT` environment variable,
the `-input-chainsync-address` option has the `INPUT_CHAINSYNC_ADDRESS`
environment variable, and `-output` has `OUTPUT`.

### Environment Variables

Core configuration options can be set using environment variables:

- `INPUT` - Input plugin to use (default: "chainsync")
- `OUTPUT` - Output plugin to use (default: "log")
- `KUPO_URL` - URL for Kupo service integration
- `LOGGING_LEVEL` - Log level (default: "info")
- `API_ADDRESS` - API server listen address (default: "0.0.0.0")
- `API_PORT` - API server port (default: 8080)
- `DEBUG_ADDRESS` - Debug server address (default: "localhost")
- `DEBUG_PORT` - Debug server port (default: 0)

Genesis configuration can also be controlled via environment variables:

**Network Transition:**
- `SHELLEY_TRANS_EPOCH` - Epoch number when Shelley era begins (default: 208 for mainnet)

**Byron Genesis:**
- `BYRON_GENESIS_END_SLOT` - End slot for Byron era
- `BYRON_GENESIS_EPOCH_LENGTH` - Slot length of Byron epochs (default: 21600)
- `BYRON_GENESIS_BYRON_SLOTS_PER_EPOCH` - Byron slots per epoch

**Shelley Genesis:**
- `SHELLEY_GENESIS_EPOCH_LENGTH` - Slot length of Shelley epochs (default: 432000)

You can also specify each option in the config file.

```yaml
Expand Down
3 changes: 2 additions & 1 deletion api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"

"github.com/blinklabs-io/adder/api"
"github.com/blinklabs-io/adder/output/push"
"github.com/stretchr/testify/assert"
)

func TestRouteRegistration(t *testing.T) {
Expand Down
15 changes: 8 additions & 7 deletions filter/chainsync/chainsync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import (
"testing"
"time"

"github.com/blinklabs-io/adder/event"
"github.com/blinklabs-io/gouroboros/cbor"
"github.com/blinklabs-io/gouroboros/ledger"
"github.com/blinklabs-io/gouroboros/ledger/common"
"github.com/blinklabs-io/plutigo/data"
"github.com/btcsuite/btcd/btcutil/bech32"
"github.com/stretchr/testify/assert"
"github.com/utxorpc/go-codegen/utxorpc/v1alpha/cardano"

"github.com/blinklabs-io/adder/event"
)

// MockLogger is a mock implementation of the plugin.Logger interface
Expand Down Expand Up @@ -92,10 +93,10 @@ func (m *MockAddress) UnmarshalCBOR(data []byte) error {
// MockOutput is a mock implementation of the TransactionOutput interface
type MockOutput struct {
address ledger.Address
amount uint64
scriptRef common.Script
assets *common.MultiAsset[common.MultiAssetTypeOutput]
datum *common.Datum
scriptRef common.Script
amount uint64
}

func (m MockOutput) Address() ledger.Address {
Expand Down Expand Up @@ -207,15 +208,15 @@ func TestChainSync_OutputChan(t *testing.T) {

// Mock certificate implementations
type mockStakeDelegationCert struct {
common.StakeDelegationCertificate
cborData []byte
common.StakeDelegationCertificate
}

func (m *mockStakeDelegationCert) Cbor() []byte { return m.cborData }

type mockStakeDeregistrationCert struct {
common.StakeDeregistrationCertificate
cborData []byte
common.StakeDeregistrationCertificate
}

func (m *mockStakeDeregistrationCert) Cbor() []byte { return m.cborData }
Expand Down Expand Up @@ -251,10 +252,10 @@ func TestFilterByAddress(t *testing.T) {
testStakeAddress, _ := bech32.Encode("stake", convData)

tests := []struct {
name string
filterAddress string
outputAddr common.Address
cert ledger.Certificate
name string
filterAddress string
shouldMatch bool
}{
{
Expand Down
3 changes: 2 additions & 1 deletion filter/chainsync/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"

"github.com/blinklabs-io/adder/event"
"github.com/blinklabs-io/adder/plugin"
"github.com/stretchr/testify/assert"
)

func TestPluginRegistration(t *testing.T) {
Expand Down
19 changes: 10 additions & 9 deletions input/chainsync/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package chainsync
import (
"testing"

"github.com/blinklabs-io/adder/event"
"github.com/blinklabs-io/gouroboros/ledger/common"
"github.com/stretchr/testify/assert"
utxorpc "github.com/utxorpc/go-codegen/utxorpc/v1alpha/cardano"

"github.com/blinklabs-io/adder/event"
)

// MockIssuerVkey to implement IssuerVkey interface
Expand All @@ -22,14 +23,14 @@ func (m MockIssuerVkey) Hash() []byte {

// MockBlockHeader implements BlockHeader interface
type MockBlockHeader struct {
hash common.Blake2b256
prevHash common.Blake2b256
era common.Era
cborBytes []byte
blockNumber uint64
slotNumber uint64
issuerVkey common.IssuerVkey
blockBodySize uint64
era common.Era
cborBytes []byte
hash common.Blake2b256
prevHash common.Blake2b256
issuerVkey common.IssuerVkey
}

func (m MockBlockHeader) Hash() common.Blake2b256 {
Expand Down Expand Up @@ -113,11 +114,11 @@ func (m MockBlock) IsConway() bool {
func TestNewBlockContext(t *testing.T) {
testCases := []struct {
name string
block MockBlock
networkMagic uint32
expectedEra string
block MockBlock
expectedBlock uint64
expectedSlot uint64
networkMagic uint32
}{
{
name: "Shelley Era Block",
Expand Down Expand Up @@ -230,9 +231,9 @@ func TestNewBlockContext(t *testing.T) {
func TestNewBlockContextEdgeCases(t *testing.T) {
testCases := []struct {
name string
expectedEra string
block MockBlock
networkMagic uint32
expectedEra string
}{
{
name: "Zero Values",
Expand Down
46 changes: 41 additions & 5 deletions input/chainsync/chainsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,52 @@ import (
"time"

"github.com/SundaeSwap-finance/kugo"
"github.com/SundaeSwap-finance/ogmigo/v6/ouroboros/chainsync"
"github.com/blinklabs-io/adder/event"
"github.com/blinklabs-io/adder/internal/config"
"github.com/blinklabs-io/adder/internal/logging"
"github.com/blinklabs-io/adder/plugin"
ouroboros "github.com/blinklabs-io/gouroboros"
"github.com/blinklabs-io/gouroboros/ledger"
"github.com/blinklabs-io/gouroboros/protocol/blockfetch"
blockfetch "github.com/blinklabs-io/gouroboros/protocol/blockfetch"
ochainsync "github.com/blinklabs-io/gouroboros/protocol/chainsync"
ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
)

// EpochFromSlot derives an epoch from a slot using Byron/Shelley genesis params.
// Byron slots: 0..EndSlot inclusive. Explicit zero EndSlot/ShelleyTransEpoch means no Byron era.
// Zero epoch length in either era yields a safe fallback (0 Byron / starting Shelley epoch).
func EpochFromSlot(slot uint64) uint64 {
cfg := config.GetConfig()
byron := cfg.ByronGenesis
shelley := cfg.ShelleyGenesis

endSlot := func() uint64 {
if byron.EndSlot != nil {
return *byron.EndSlot
}
return 0
}()
shelleyTransEpoch := func() uint64 {
if cfg.ShelleyTransEpoch >= 0 {
//nolint:gosec // ShelleyTransEpoch is controlled config, safe conversion
return uint64(cfg.ShelleyTransEpoch)
}
return 0
}()
if slot <= endSlot {
if byron.EpochLength == 0 {
return 0 // avoid div by zero
}
return slot / byron.EpochLength
}
shelleyStartEpoch := shelleyTransEpoch
shelleyStartSlot := endSlot + 1
if shelley.EpochLength == 0 {
return shelleyStartEpoch // avoid div by zero
}
return shelleyStartEpoch + (slot-shelleyStartSlot)/shelley.EpochLength
}

const (
// Size of cache for recent chainsync cursors
cursorCacheSize = 20
Expand Down Expand Up @@ -84,6 +118,7 @@ type ChainSyncStatus struct {
TipBlockHash string
SlotNumber uint64
BlockNumber uint64
EpochNumber uint64
TipSlotNumber uint64
TipReached bool
}
Expand Down Expand Up @@ -522,6 +557,7 @@ func (c *ChainSync) updateStatus(
c.status.SlotNumber = slotNumber
c.status.BlockNumber = blockNumber
c.status.BlockHash = blockHash
c.status.EpochNumber = EpochFromSlot(slotNumber)
c.status.TipSlotNumber = tipSlotNumber
c.status.TipBlockHash = tipBlockHash
if c.statusUpdateFunc != nil {
Expand Down Expand Up @@ -621,9 +657,9 @@ func resolveTransactionInputs(
)
defer cancel()

matches, err := k.Matches(ctx,
kugo.TxOut(chainsync.NewTxID(txId, txIndex)),
)
// Create a simple transaction identifier
txID := fmt.Sprintf("%d@%s", txIndex, txId)
matches, err := k.Matches(ctx, kugo.Transaction(txID))
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return nil, fmt.Errorf(
Expand Down
22 changes: 19 additions & 3 deletions input/chainsync/chainsync_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
// Copyright 2025 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package chainsync

import (
Expand All @@ -10,11 +23,12 @@ import (
"time"

"github.com/SundaeSwap-finance/kugo"
"github.com/blinklabs-io/adder/event"
"github.com/blinklabs-io/gouroboros/protocol/chainsync"
ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/blinklabs-io/adder/event"
)

func TestHandleRollBackward(t *testing.T) {
Expand All @@ -26,7 +40,7 @@ func TestHandleRollBackward(t *testing.T) {

// Define test data
point := ocommon.Point{
Slot: 12345,
Slot: 123456,
Hash: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
}
tip := chainsync.Tip{
Expand Down Expand Up @@ -64,7 +78,7 @@ func TestHandleRollBackward(t *testing.T) {
}

// Verify that the status was updated correctly
assert.Equal(t, uint64(12345), c.status.SlotNumber)
assert.Equal(t, uint64(123456), c.status.SlotNumber)
assert.Equal(
t,
uint64(0),
Expand All @@ -73,6 +87,8 @@ func TestHandleRollBackward(t *testing.T) {
assert.Equal(t, "0102030405", c.status.BlockHash)
assert.Equal(t, uint64(67890), c.status.TipSlotNumber)
assert.Equal(t, "060708090a", c.status.TipBlockHash)
// New: Check EpochNumber (Byron era: 123456/21600 = 5)
assert.Equal(t, uint64(5), c.status.EpochNumber)
}

func TestGetKupoClient(t *testing.T) {
Expand Down
Loading
Loading