Skip to content
Draft
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
14 changes: 7 additions & 7 deletions .github/workflows/horizon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ jobs:
PROTOCOL_27_CORE_DOCKER_IMG: stellar/stellar-core:27.0.0-3288.7696c069d.jammy
PROTOCOL_27_CORE_DEBIAN_PKG_VERSION: 27.0.0-3288.7696c069d.jammy~buildtests
PROTOCOL_27_STELLAR_RPC_DOCKER_IMG: stellar/stellar-rpc:27.0.0-preview-190
# Protocol 28 (CAP-0083) runs against the vnext (next-protocol-enabled) core build.
# Pinned to 3367 (3589a696b), which includes stellar-core#5329 — the fix
# for the QueryServer.cpp:124 contiguous-snapshot assertion that aborted
# captive-core catchup on builds 3313..3366.
PROTOCOL_28_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:27.0.1-3367.3589a696b.jammy-vnext
PROTOCOL_28_CORE_DEBIAN_PKG_VERSION: 27.0.1-3367.3589a696b.jammy~vnext~buildtests
# No Protocol 28 stellar-rpc image is published yet (tracked: stellar-rpc#789).
# Protocol 28 (CAP-0084) runs against the vnext (next-protocol-enabled) core build.
# Pinned to build 3395 (4cdd2fc9e) — the -vnext image/deb matching CAP-0084
# core (stellar-core#5337, HEAD 4cdd2fc9e). Variant preserved from the p27 leg:
# plain -vnext docker + ~vnext~buildtests deb.
PROTOCOL_28_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:27.1.1-3395.4cdd2fc9e.jammy-vnext
PROTOCOL_28_CORE_DEBIAN_PKG_VERSION: 27.1.1-3395.4cdd2fc9e.jammy~vnext~buildtests
# No Protocol 28 stellar-rpc image is published yet (tracked: stellar-rpc#837).
# Leave EMPTY on purpose: the P27 RPC image bundles a P27 captive-core that
# cannot ingest a protocol-28 network, so bringing it up makes every
# EnableStellarRPC test hang in waitForStellarRPC() ("DB is empty") until the
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ file. This project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased

### Added
- Added Protocol 28 ingestion support for CAP-0084 (Muxed Contract Addresses):
SAC transfer/mint events to a muxed-contract destination surface the base
contract in the `to` topic and the mux id via `to_muxed_id`, and bumped
`MaxSupportedProtocolVersion` to 28 ([#203](https://github.com/stellar/stellar-horizon/pull/203)).

## 27.0.0

**This release adds support for Protocol 27**
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,5 @@ require (
google.golang.org/protobuf v1.36.10
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/stellar/go-stellar-sdk => github.com/sisuresh/go v0.0.0-20260701230520-eeca7aa4a3bc
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+Yg
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sisuresh/go v0.0.0-20260701230520-eeca7aa4a3bc h1:WVa7+1/bWNhqwKTBIZ+yHdANMwmXDb4VOym4bn2YpME=
github.com/sisuresh/go v0.0.0-20260701230520-eeca7aa4a3bc/go.mod h1:IkcqcrE9UQi7n/1y+MxKB+7qzdjG1T2kGOD7Ss8dqjw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
Expand All @@ -429,8 +431,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
github.com/stellar/go-stellar-sdk v0.6.1-0.20260616165505-26fec7c0e891 h1:Y/PtLm4L4ZHLDI1dXIflRjI5pY+m+KD7FUVFM07tV+E=
github.com/stellar/go-stellar-sdk v0.6.1-0.20260616165505-26fec7c0e891/go.mod h1:IkcqcrE9UQi7n/1y+MxKB+7qzdjG1T2kGOD7Ss8dqjw=
github.com/stellar/go-xdr v0.0.0-20260529210834-0bf8f4956364 h1:gOKrfuWdZ92LFlv0TAwgZ7OsWKeBsOMDlGLyFgduI1w=
github.com/stellar/go-xdr v0.0.0-20260529210834-0bf8f4956364/go.mod h1:If+U9Z1W5xU97VrOgJandQT+2dN7/iOpkCrxBJEyF80=
github.com/stellar/throttled v2.2.3-0.20190823235211-89d75816f59d+incompatible h1:jMXXAcz6xTarGDQ4VtVbtERogcmDQw4RaE85Cr9CgoQ=
Expand Down
21 changes: 14 additions & 7 deletions internal/ingest/contractevents/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,15 @@ func parseSacEventFromTxMetaV4(event *xdr.ContractEvent, networkPassphrase strin
}

// parseSacEventMap parses the ScMap data format used in V4 SAC events.
// For SAC events, to_muxed_id represents the muxed account ID from
// MuxedAddressObject - which is always a uint64. ScvBytes and ScvString are NOT
// valid for SAC events (those are only used for classic transaction memo mappings
// per CAP-67, which are processed through a different code path).
// For SAC events, to_muxed_id is always a uint64. It carries either the muxed
// account ID from a MuxedAddressObject (CAP-67) or, under CAP-0084, the muxed
// contract ID from a MUXED_CONTRACT ScAddress. In both cases the SAC host
// de-muxes the destination before emitting the event: the `to` topic holds the
// base (account or contract) address and the mux id is surfaced separately as a
// uint64, so the two are byte-identical in shape and indistinguishable from the
// event alone. This holds for both transfer and mint. ScvBytes and ScvString are
// NOT valid for SAC events (those are only used for classic transaction memo
// mappings per CAP-67, which are processed through a different code path).
func parseSacEventMap(mapData xdr.ScMap) (xdr.Int128Parts, xdr.Memo, error) {
var foundAmount, foundMuxedId bool
var amount xdr.Int128Parts
Expand All @@ -346,9 +351,11 @@ func parseSacEventMap(mapData xdr.ScMap) (xdr.Int128Parts, xdr.Memo, error) {

case "to_muxed_id":
foundMuxedId = true
// SAC events only emit uint64 for to_muxed_id (muxed account ID).
// ScvBytes/ScvString are NOT valid here - those are only for classic
// transaction memo mappings which use a different code path.
// SAC events only emit uint64 for to_muxed_id (muxed account ID under
// CAP-67, or muxed contract ID under CAP-0084 - both de-muxed by the
// host to a uint64). ScvBytes/ScvString are NOT valid here - those are
// only for classic transaction memo mappings which use a different
// code path.
switch entry.Val.Type {
case xdr.ScValTypeScvU64:
if val, ok := entry.Val.GetU64(); ok {
Expand Down
54 changes: 54 additions & 0 deletions internal/ingest/contractevents/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ var (
randomAccount = keypair.MustRandom().Address()
zeroContractHash = xdr.Hash([32]byte{})
zeroContract = strkey.MustEncode(strkey.VersionByteContract, zeroContractHash[:])
// muxedContractHash stands in for the base contract a CAP-0084
// MUXED_CONTRACT address de-muxes to. The host emits this base contract in
// the `to` topic and surfaces the mux id separately as a uint64.
muxedContractHash = xdr.Hash([32]byte{0xCA, 0xFE, 0xBA, 0xBE})
muxedContract = strkey.MustEncode(strkey.VersionByteContract, muxedContractHash[:])
)

// Test fixture structure
Expand Down Expand Up @@ -242,6 +247,55 @@ func TestStellarAssetContractEventParsing(t *testing.T) {
DestinationMemo: xdr.MemoID(12345),
},
},
{
// CAP-0084: a transfer to a muxed contract destination. The host
// de-muxes the MUXED_CONTRACT address, emitting the base contract in
// the `to` topic and the mux id as a uint64 to_muxed_id - byte-identical
// in shape to the CAP-67 muxed-account case above, by design.
name: "V4 transfer to muxed contract (CAP-0084)",
txMetaVersion: 4,
eventType: EventTypeTransfer,
topics: []xdr.ScVal{
makeSymbol("transfer"),
makeAddress(randomAccount),
makeAddress(muxedContract),
makeAsset(randomAsset),
},
data: makeV4MapData(big.NewInt(1000), xdr.MemoID(67890)),
asset: randomAsset,
contractID: mustGetContractID(randomAsset),
expectedResult: &StellarAssetContractEvent{
Type: EventTypeTransfer,
Asset: randomAsset,
From: randomAccount,
To: muxedContract,
Amount: xdr.Int128Parts{Lo: 1000, Hi: 0},
DestinationMemo: xdr.MemoID(67890),
},
},
{
// CAP-0084: a mint to a muxed contract destination. The mint path
// de-muxes the destination identically to transfer, so the muxed
// contract id surfaces as a uint64 to_muxed_id as well.
name: "V4 mint to muxed contract (CAP-0084)",
txMetaVersion: 4,
eventType: EventTypeMint,
topics: []xdr.ScVal{
makeSymbol("mint"),
makeAddress(muxedContract), // to (no admin in V4)
makeAsset(randomAsset),
},
data: makeV4MapData(big.NewInt(2000), xdr.MemoID(67890)),
asset: randomAsset,
contractID: mustGetContractID(randomAsset),
expectedResult: &StellarAssetContractEvent{
Type: EventTypeMint,
Asset: randomAsset,
To: muxedContract,
Amount: xdr.Int128Parts{Lo: 2000, Hi: 0},
DestinationMemo: xdr.MemoID(67890),
},
},
{
name: "V4 SAC event rejects ScvString for to_muxed_id",
txMetaVersion: 4,
Expand Down
Binary file not shown.
Binary file not shown.
Loading