From 2aa17029530f1e8f5d753307faa44d1746462864 Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Wed, 23 Sep 2026 14:01:32 -0700 Subject: [PATCH 01/10] xdr: regenerate from stellar-xdr@01741cb6 with CAP_0084_MUXED_CONTRACT Adds the SC_ADDRESS_TYPE_MUXED_CONTRACT arm of SCAddress. Also picks up the widened contract spec name limits from upstream. --- Makefile | 8 +- gxdr/xdr_generated.go | 80 ++++++++++++--- xdr/Stellar-contract-spec.x | 14 +-- xdr/Stellar-contract.x | 9 ++ xdr/xdr_commit_generated.txt | 2 +- xdr/xdr_generated.go | 185 ++++++++++++++++++++++++++++----- xdr/xdr_views_generated.go | 193 ++++++++++++++++++++++++++++++----- 7 files changed, 418 insertions(+), 73 deletions(-) diff --git a/Makefile b/Makefile index 36b9874a38..c27313d4b9 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,9 @@ xdr/Stellar-exporter.x XDRGEN_COMMIT=b423e1da9504239fb3136cbcc5f9beeb37795837 -# Protocol 28 GA: stellar-xdr@9c9c1459, the commit stellar-core 28.0.0 pins as -# src/protocol-curr/xdr (v28.0 tag pending; CAP-0083 + CAP-0085 are ungated here). -XDR_COMMIT=9c9c145953e80990d6ff1ae3a6a973a0ce6d0694 +# Protocol 30: stellar-xdr@01741cb6, the commit stellar-core and rs-stellar-xdr pin +# for CAP-0084 (gated behind CAP_0084_MUXED_CONTRACT, see XDR_FEATURES below). +XDR_COMMIT=01741cb652724526d3f90ffe39b417d6a88ee819 .PHONY: xdr xdr-clean xdr-update @@ -69,7 +69,7 @@ xdr/xdr_generated.go: $(XDRS) # Optional comma-separated features for #ifdef resolution in the XDR files. # Empty = no features enabled; only unconditional definitions are emitted. -XDR_FEATURES ?= +XDR_FEATURES ?= CAP_0084_MUXED_CONTRACT # Generates xdr/xdr_views_generated.go via a two-step pipeline: # 1. The rust `generator-definitions-json` tool parses the .x files and diff --git a/gxdr/xdr_generated.go b/gxdr/xdr_generated.go index 5428f4e620..be6cbe867a 100644 --- a/gxdr/xdr_generated.go +++ b/gxdr/xdr_generated.go @@ -4079,6 +4079,8 @@ type SCMetaEntry struct { const SC_SPEC_DOC_LIMIT = 1024 +const SC_SPEC_TYPE_NAME_LIMIT = 1024 + type SCSpecType int32 const ( @@ -4140,7 +4142,7 @@ type SCSpecTypeBytesN struct { } type SCSpecTypeUDT struct { - Name string // bound 60 + Name string // bound SC_SPEC_TYPE_NAME_LIMIT } type SCSpecTypeDef struct { @@ -4174,7 +4176,7 @@ type SCSpecUDTStructFieldV0 struct { type SCSpecUDTStructV0 struct { Doc string // bound SC_SPEC_DOC_LIMIT Lib string // bound 80 - Name string // bound 60 + Name string // bound SC_SPEC_TYPE_NAME_LIMIT Fields []SCSpecUDTStructFieldV0 } @@ -4209,7 +4211,7 @@ type SCSpecUDTUnionCaseV0 struct { type SCSpecUDTUnionV0 struct { Doc string // bound SC_SPEC_DOC_LIMIT Lib string // bound 80 - Name string // bound 60 + Name string // bound SC_SPEC_TYPE_NAME_LIMIT Cases []SCSpecUDTUnionCaseV0 } @@ -4222,7 +4224,7 @@ type SCSpecUDTEnumCaseV0 struct { type SCSpecUDTEnumV0 struct { Doc string // bound SC_SPEC_DOC_LIMIT Lib string // bound 80 - Name string // bound 60 + Name string // bound SC_SPEC_TYPE_NAME_LIMIT Cases []SCSpecUDTEnumCaseV0 } @@ -4235,7 +4237,7 @@ type SCSpecUDTErrorEnumCaseV0 struct { type SCSpecUDTErrorEnumV0 struct { Doc string // bound SC_SPEC_DOC_LIMIT Lib string // bound 80 - Name string // bound 60 + Name string // bound SC_SPEC_TYPE_NAME_LIMIT Cases []SCSpecUDTErrorEnumCaseV0 } @@ -4275,9 +4277,9 @@ const ( ) type SCSpecEventV0 struct { - Doc string // bound SC_SPEC_DOC_LIMIT - Lib string // bound 80 - Name SCSymbol + Doc string // bound SC_SPEC_DOC_LIMIT + Lib string // bound 80 + Name string // bound SC_SPEC_TYPE_NAME_LIMIT PrefixTopics []SCSymbol // bound 2 Params []SCSpecEventParamV0 DataFormat SCSpecEventDataFormat @@ -4475,6 +4477,7 @@ const ( SC_ADDRESS_TYPE_MUXED_ACCOUNT SCAddressType = 2 SC_ADDRESS_TYPE_CLAIMABLE_BALANCE SCAddressType = 3 SC_ADDRESS_TYPE_LIQUIDITY_POOL SCAddressType = 4 + SC_ADDRESS_TYPE_MUXED_CONTRACT SCAddressType = 5 ) type MuxedEd25519Account struct { @@ -4482,6 +4485,11 @@ type MuxedEd25519Account struct { Ed25519 Uint256 } +type MuxedContract struct { + Id Uint64 + ContractId ContractID +} + type SCAddress struct { // The union discriminant Type selects among the following arms: // SC_ADDRESS_TYPE_ACCOUNT: @@ -4494,6 +4502,8 @@ type SCAddress struct { // ClaimableBalanceId() *ClaimableBalanceID // SC_ADDRESS_TYPE_LIQUIDITY_POOL: // LiquidityPoolId() *PoolID + // SC_ADDRESS_TYPE_MUXED_CONTRACT: + // MuxedContract() *MuxedContract Type SCAddressType _u interface{} } @@ -28116,7 +28126,7 @@ func (v *SCSpecTypeUDT) XdrRecurse(x XDR, name string) { if name != "" { name = x.Sprintf("%s.", name) } - x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, 60}) + x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, SC_SPEC_TYPE_NAME_LIMIT}) } func XDR_SCSpecTypeUDT(v *SCSpecTypeUDT) *SCSpecTypeUDT { return v } @@ -28444,7 +28454,7 @@ func (v *SCSpecUDTStructV0) XdrRecurse(x XDR, name string) { } x.Marshal(x.Sprintf("%sdoc", name), XdrString{&v.Doc, SC_SPEC_DOC_LIMIT}) x.Marshal(x.Sprintf("%slib", name), XdrString{&v.Lib, 80}) - x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, 60}) + x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, SC_SPEC_TYPE_NAME_LIMIT}) x.Marshal(x.Sprintf("%sfields", name), (*_XdrVec_unbounded_SCSpecUDTStructFieldV0)(&v.Fields)) } func XDR_SCSpecUDTStructV0(v *SCSpecUDTStructV0) *SCSpecUDTStructV0 { return v } @@ -28748,7 +28758,7 @@ func (v *SCSpecUDTUnionV0) XdrRecurse(x XDR, name string) { } x.Marshal(x.Sprintf("%sdoc", name), XdrString{&v.Doc, SC_SPEC_DOC_LIMIT}) x.Marshal(x.Sprintf("%slib", name), XdrString{&v.Lib, 80}) - x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, 60}) + x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, SC_SPEC_TYPE_NAME_LIMIT}) x.Marshal(x.Sprintf("%scases", name), (*_XdrVec_unbounded_SCSpecUDTUnionCaseV0)(&v.Cases)) } func XDR_SCSpecUDTUnionV0(v *SCSpecUDTUnionV0) *SCSpecUDTUnionV0 { return v } @@ -28842,7 +28852,7 @@ func (v *SCSpecUDTEnumV0) XdrRecurse(x XDR, name string) { } x.Marshal(x.Sprintf("%sdoc", name), XdrString{&v.Doc, SC_SPEC_DOC_LIMIT}) x.Marshal(x.Sprintf("%slib", name), XdrString{&v.Lib, 80}) - x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, 60}) + x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, SC_SPEC_TYPE_NAME_LIMIT}) x.Marshal(x.Sprintf("%scases", name), (*_XdrVec_unbounded_SCSpecUDTEnumCaseV0)(&v.Cases)) } func XDR_SCSpecUDTEnumV0(v *SCSpecUDTEnumV0) *SCSpecUDTEnumV0 { return v } @@ -28940,7 +28950,7 @@ func (v *SCSpecUDTErrorEnumV0) XdrRecurse(x XDR, name string) { } x.Marshal(x.Sprintf("%sdoc", name), XdrString{&v.Doc, SC_SPEC_DOC_LIMIT}) x.Marshal(x.Sprintf("%slib", name), XdrString{&v.Lib, 80}) - x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, 60}) + x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, SC_SPEC_TYPE_NAME_LIMIT}) x.Marshal(x.Sprintf("%scases", name), (*_XdrVec_unbounded_SCSpecUDTErrorEnumCaseV0)(&v.Cases)) } func XDR_SCSpecUDTErrorEnumV0(v *SCSpecUDTErrorEnumV0) *SCSpecUDTErrorEnumV0 { return v } @@ -29337,7 +29347,7 @@ func (v *SCSpecEventV0) XdrRecurse(x XDR, name string) { } x.Marshal(x.Sprintf("%sdoc", name), XdrString{&v.Doc, SC_SPEC_DOC_LIMIT}) x.Marshal(x.Sprintf("%slib", name), XdrString{&v.Lib, 80}) - x.Marshal(x.Sprintf("%sname", name), XDR_SCSymbol(&v.Name)) + x.Marshal(x.Sprintf("%sname", name), XdrString{&v.Name, SC_SPEC_TYPE_NAME_LIMIT}) x.Marshal(x.Sprintf("%sprefixTopics", name), (*_XdrVec_2_SCSymbol)(&v.PrefixTopics)) x.Marshal(x.Sprintf("%sparams", name), (*_XdrVec_unbounded_SCSpecEventParamV0)(&v.Params)) x.Marshal(x.Sprintf("%sdataFormat", name), XDR_SCSpecEventDataFormat(&v.DataFormat)) @@ -30090,6 +30100,7 @@ var _XdrNames_SCAddressType = map[int32]string{ int32(SC_ADDRESS_TYPE_MUXED_ACCOUNT): "SC_ADDRESS_TYPE_MUXED_ACCOUNT", int32(SC_ADDRESS_TYPE_CLAIMABLE_BALANCE): "SC_ADDRESS_TYPE_CLAIMABLE_BALANCE", int32(SC_ADDRESS_TYPE_LIQUIDITY_POOL): "SC_ADDRESS_TYPE_LIQUIDITY_POOL", + int32(SC_ADDRESS_TYPE_MUXED_CONTRACT): "SC_ADDRESS_TYPE_MUXED_CONTRACT", } var _XdrValues_SCAddressType = map[string]int32{ "SC_ADDRESS_TYPE_ACCOUNT": int32(SC_ADDRESS_TYPE_ACCOUNT), @@ -30097,6 +30108,7 @@ var _XdrValues_SCAddressType = map[string]int32{ "SC_ADDRESS_TYPE_MUXED_ACCOUNT": int32(SC_ADDRESS_TYPE_MUXED_ACCOUNT), "SC_ADDRESS_TYPE_CLAIMABLE_BALANCE": int32(SC_ADDRESS_TYPE_CLAIMABLE_BALANCE), "SC_ADDRESS_TYPE_LIQUIDITY_POOL": int32(SC_ADDRESS_TYPE_LIQUIDITY_POOL), + "SC_ADDRESS_TYPE_MUXED_CONTRACT": int32(SC_ADDRESS_TYPE_MUXED_CONTRACT), } func (SCAddressType) XdrEnumNames() map[int32]string { @@ -30150,12 +30162,28 @@ func (v *MuxedEd25519Account) XdrRecurse(x XDR, name string) { } func XDR_MuxedEd25519Account(v *MuxedEd25519Account) *MuxedEd25519Account { return v } +type XdrType_MuxedContract = *MuxedContract + +func (v *MuxedContract) XdrPointer() interface{} { return v } +func (MuxedContract) XdrTypeName() string { return "MuxedContract" } +func (v MuxedContract) XdrValue() interface{} { return v } +func (v *MuxedContract) XdrMarshal(x XDR, name string) { x.Marshal(name, v) } +func (v *MuxedContract) XdrRecurse(x XDR, name string) { + if name != "" { + name = x.Sprintf("%s.", name) + } + x.Marshal(x.Sprintf("%sid", name), XDR_Uint64(&v.Id)) + x.Marshal(x.Sprintf("%scontractId", name), XDR_ContractID(&v.ContractId)) +} +func XDR_MuxedContract(v *MuxedContract) *MuxedContract { return v } + var _XdrTags_SCAddress = map[int32]bool{ XdrToI32(SC_ADDRESS_TYPE_ACCOUNT): true, XdrToI32(SC_ADDRESS_TYPE_CONTRACT): true, XdrToI32(SC_ADDRESS_TYPE_MUXED_ACCOUNT): true, XdrToI32(SC_ADDRESS_TYPE_CLAIMABLE_BALANCE): true, XdrToI32(SC_ADDRESS_TYPE_LIQUIDITY_POOL): true, + XdrToI32(SC_ADDRESS_TYPE_MUXED_CONTRACT): true, } func (_ SCAddress) XdrValidTags() map[int32]bool { @@ -30236,9 +30264,24 @@ func (u *SCAddress) LiquidityPoolId() *PoolID { return nil } } +func (u *SCAddress) MuxedContract() *MuxedContract { + switch u.Type { + case SC_ADDRESS_TYPE_MUXED_CONTRACT: + if v, ok := u._u.(*MuxedContract); ok { + return v + } else { + var zero MuxedContract + u._u = &zero + return &zero + } + default: + XdrPanic("SCAddress.MuxedContract accessed when Type == %v", u.Type) + return nil + } +} func (u SCAddress) XdrValid() bool { switch u.Type { - case SC_ADDRESS_TYPE_ACCOUNT, SC_ADDRESS_TYPE_CONTRACT, SC_ADDRESS_TYPE_MUXED_ACCOUNT, SC_ADDRESS_TYPE_CLAIMABLE_BALANCE, SC_ADDRESS_TYPE_LIQUIDITY_POOL: + case SC_ADDRESS_TYPE_ACCOUNT, SC_ADDRESS_TYPE_CONTRACT, SC_ADDRESS_TYPE_MUXED_ACCOUNT, SC_ADDRESS_TYPE_CLAIMABLE_BALANCE, SC_ADDRESS_TYPE_LIQUIDITY_POOL, SC_ADDRESS_TYPE_MUXED_CONTRACT: return true } return false @@ -30261,6 +30304,8 @@ func (u *SCAddress) XdrUnionBody() XdrType { return XDR_ClaimableBalanceID(u.ClaimableBalanceId()) case SC_ADDRESS_TYPE_LIQUIDITY_POOL: return XDR_PoolID(u.LiquidityPoolId()) + case SC_ADDRESS_TYPE_MUXED_CONTRACT: + return XDR_MuxedContract(u.MuxedContract()) } return nil } @@ -30276,6 +30321,8 @@ func (u *SCAddress) XdrUnionBodyName() string { return "ClaimableBalanceId" case SC_ADDRESS_TYPE_LIQUIDITY_POOL: return "LiquidityPoolId" + case SC_ADDRESS_TYPE_MUXED_CONTRACT: + return "MuxedContract" } return "" } @@ -30307,6 +30354,9 @@ func (u *SCAddress) XdrRecurse(x XDR, name string) { case SC_ADDRESS_TYPE_LIQUIDITY_POOL: x.Marshal(x.Sprintf("%sliquidityPoolId", name), XDR_PoolID(u.LiquidityPoolId())) return + case SC_ADDRESS_TYPE_MUXED_CONTRACT: + x.Marshal(x.Sprintf("%smuxedContract", name), XDR_MuxedContract(u.MuxedContract())) + return } XdrPanic("invalid Type (%v) in SCAddress", u.Type) } diff --git a/xdr/Stellar-contract-spec.x b/xdr/Stellar-contract-spec.x index 33c4cec1e2..d87e643e4f 100644 --- a/xdr/Stellar-contract-spec.x +++ b/xdr/Stellar-contract-spec.x @@ -12,6 +12,8 @@ namespace stellar const SC_SPEC_DOC_LIMIT = 1024; +const SC_SPEC_TYPE_NAME_LIMIT = 1024; + enum SCSpecType { SC_SPEC_TYPE_VAL = 0, @@ -82,7 +84,7 @@ struct SCSpecTypeBytesN struct SCSpecTypeUDT { - string name<60>; + string name; }; union SCSpecTypeDef switch (SCSpecType type) @@ -134,7 +136,7 @@ struct SCSpecUDTStructV0 { string doc; string lib<80>; - string name<60>; + string name; SCSpecUDTStructFieldV0 fields<>; }; @@ -169,7 +171,7 @@ struct SCSpecUDTUnionV0 { string doc; string lib<80>; - string name<60>; + string name; SCSpecUDTUnionCaseV0 cases<>; }; @@ -184,7 +186,7 @@ struct SCSpecUDTEnumV0 { string doc; string lib<80>; - string name<60>; + string name; SCSpecUDTEnumCaseV0 cases<>; }; @@ -199,7 +201,7 @@ struct SCSpecUDTErrorEnumV0 { string doc; string lib<80>; - string name<60>; + string name; SCSpecUDTErrorEnumCaseV0 cases<>; }; @@ -243,7 +245,7 @@ struct SCSpecEventV0 { string doc; string lib<80>; - SCSymbol name; + string name; SCSymbol prefixTopics<2>; SCSpecEventParamV0 params<>; SCSpecEventDataFormat dataFormat; diff --git a/xdr/Stellar-contract.x b/xdr/Stellar-contract.x index 9630ddf05e..a891fd0375 100644 --- a/xdr/Stellar-contract.x +++ b/xdr/Stellar-contract.x @@ -183,6 +183,8 @@ enum SCAddressType SC_ADDRESS_TYPE_MUXED_ACCOUNT = 2, SC_ADDRESS_TYPE_CLAIMABLE_BALANCE = 3, SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4 + , + SC_ADDRESS_TYPE_MUXED_CONTRACT = 5 }; struct MuxedEd25519Account @@ -191,6 +193,11 @@ struct MuxedEd25519Account uint256 ed25519; }; +struct MuxedContract +{ + uint64 id; + ContractID contractId; +}; union SCAddress switch (SCAddressType type) { @@ -204,6 +211,8 @@ case SC_ADDRESS_TYPE_CLAIMABLE_BALANCE: ClaimableBalanceID claimableBalanceId; case SC_ADDRESS_TYPE_LIQUIDITY_POOL: PoolID liquidityPoolId; +case SC_ADDRESS_TYPE_MUXED_CONTRACT: + MuxedContract muxedContract; }; struct ContractExecutableExternalRef { diff --git a/xdr/xdr_commit_generated.txt b/xdr/xdr_commit_generated.txt index 0b697893f4..d4bdb9c937 100644 --- a/xdr/xdr_commit_generated.txt +++ b/xdr/xdr_commit_generated.txt @@ -1 +1 @@ -9c9c145953e80990d6ff1ae3a6a973a0ce6d0694 \ No newline at end of file +01741cb652724526d3f90ffe39b417d6a88ee819 \ No newline at end of file diff --git a/xdr/xdr_generated.go b/xdr/xdr_generated.go index 4c358de0aa..05ca24e934 100644 --- a/xdr/xdr_generated.go +++ b/xdr/xdr_generated.go @@ -36,8 +36,8 @@ var XdrFilesSHA256 = map[string]string{ "xdr/Stellar-contract-config-setting.x": "a034a3eb4d8b94f5c4c573fe14a1afc548aa316e1e897aa70e5a1688aada3c77", "xdr/Stellar-contract-env-meta.x": "75a271414d852096fea3283c63b7f2a702f2905f78fc28eb60ec7d7bd366a780", "xdr/Stellar-contract-meta.x": "f01532c11ca044e19d9f9f16fe373e9af64835da473be556b9a807ee3319ae0d", - "xdr/Stellar-contract-spec.x": "7d99679155f6ce029f4f2bd8e1bf09524ef2f3e4ca8973265085cfcfdbdae987", - "xdr/Stellar-contract.x": "a59c25f38b4705ae149a8f049eb686f6018712d792b9207318a644ea5b5f52b5", + "xdr/Stellar-contract-spec.x": "943e65a0a8f5e8a5b19738ca7d029ce7a38fea4b1f9f59cb7d4262094882e03d", + "xdr/Stellar-contract.x": "1efa2e593e8a0e2ebbd3fde00e73da1d1afd10891a83e101dc46318673a34bbd", "xdr/Stellar-exporter.x": "a00c83d02e8c8382e06f79a191f1fb5abd097a4bbcab8481c67467e3270e0529", "xdr/Stellar-internal.x": "227835866c1b2122d1eaf28839ba85ea7289d1cb681dda4ca619c2da3d71fe00", "xdr/Stellar-ledger-entries.x": "65a24350a69f0d1c74c0dce61a68db2a657611ad9318cb2736860fd99a2db020", @@ -53876,6 +53876,11 @@ var _ xdrType = (*ScMetaEntry)(nil) // const SC_SPEC_DOC_LIMIT = 1024; const ScSpecDocLimit = 1024 +// ScSpecTypeNameLimit is an XDR Const defines as: +// +// const SC_SPEC_TYPE_NAME_LIMIT = 1024; +const ScSpecTypeNameLimit = 1024 + // ScSpecType is an XDR Enum defines as: // // enum SCSpecType @@ -54490,10 +54495,10 @@ var _ xdrType = (*ScSpecTypeBytesN)(nil) // // struct SCSpecTypeUDT // { -// string name<60>; +// string name; // }; type ScSpecTypeUdt struct { - Name string `xdrmaxsize:"60"` + Name string `xdrmaxsize:"1024"` } // EncodeTo encodes this value using the Encoder. @@ -54515,7 +54520,7 @@ func (s *ScSpecTypeUdt) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { maxDepth -= 1 var err error var n, nTmp int - s.Name, nTmp, err = d.DecodeString(60) + s.Name, nTmp, err = d.DecodeString(1024) n += nTmp if err != nil { return n, fmt.Errorf("decoding Name: %w", err) @@ -55313,13 +55318,13 @@ var _ xdrType = (*ScSpecUdtStructFieldV0)(nil) // { // string doc; // string lib<80>; -// string name<60>; +// string name; // SCSpecUDTStructFieldV0 fields<>; // }; type ScSpecUdtStructV0 struct { Doc string `xdrmaxsize:"1024"` Lib string `xdrmaxsize:"80"` - Name string `xdrmaxsize:"60"` + Name string `xdrmaxsize:"1024"` Fields []ScSpecUdtStructFieldV0 } @@ -55366,7 +55371,7 @@ func (s *ScSpecUdtStructV0) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, erro if err != nil { return n, fmt.Errorf("decoding Lib: %w", err) } - s.Name, nTmp, err = d.DecodeString(60) + s.Name, nTmp, err = d.DecodeString(1024) n += nTmp if err != nil { return n, fmt.Errorf("decoding Name: %w", err) @@ -55914,13 +55919,13 @@ var _ xdrType = (*ScSpecUdtUnionCaseV0)(nil) // { // string doc; // string lib<80>; -// string name<60>; +// string name; // SCSpecUDTUnionCaseV0 cases<>; // }; type ScSpecUdtUnionV0 struct { Doc string `xdrmaxsize:"1024"` Lib string `xdrmaxsize:"80"` - Name string `xdrmaxsize:"60"` + Name string `xdrmaxsize:"1024"` Cases []ScSpecUdtUnionCaseV0 } @@ -55967,7 +55972,7 @@ func (s *ScSpecUdtUnionV0) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error if err != nil { return n, fmt.Errorf("decoding Lib: %w", err) } - s.Name, nTmp, err = d.DecodeString(60) + s.Name, nTmp, err = d.DecodeString(1024) n += nTmp if err != nil { return n, fmt.Errorf("decoding Name: %w", err) @@ -56125,13 +56130,13 @@ var _ xdrType = (*ScSpecUdtEnumCaseV0)(nil) // { // string doc; // string lib<80>; -// string name<60>; +// string name; // SCSpecUDTEnumCaseV0 cases<>; // }; type ScSpecUdtEnumV0 struct { Doc string `xdrmaxsize:"1024"` Lib string `xdrmaxsize:"80"` - Name string `xdrmaxsize:"60"` + Name string `xdrmaxsize:"1024"` Cases []ScSpecUdtEnumCaseV0 } @@ -56178,7 +56183,7 @@ func (s *ScSpecUdtEnumV0) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) if err != nil { return n, fmt.Errorf("decoding Lib: %w", err) } - s.Name, nTmp, err = d.DecodeString(60) + s.Name, nTmp, err = d.DecodeString(1024) n += nTmp if err != nil { return n, fmt.Errorf("decoding Name: %w", err) @@ -56336,13 +56341,13 @@ var _ xdrType = (*ScSpecUdtErrorEnumCaseV0)(nil) // { // string doc; // string lib<80>; -// string name<60>; +// string name; // SCSpecUDTErrorEnumCaseV0 cases<>; // }; type ScSpecUdtErrorEnumV0 struct { Doc string `xdrmaxsize:"1024"` Lib string `xdrmaxsize:"80"` - Name string `xdrmaxsize:"60"` + Name string `xdrmaxsize:"1024"` Cases []ScSpecUdtErrorEnumCaseV0 } @@ -56389,7 +56394,7 @@ func (s *ScSpecUdtErrorEnumV0) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, e if err != nil { return n, fmt.Errorf("decoding Lib: %w", err) } - s.Name, nTmp, err = d.DecodeString(60) + s.Name, nTmp, err = d.DecodeString(1024) n += nTmp if err != nil { return n, fmt.Errorf("decoding Name: %w", err) @@ -56980,15 +56985,15 @@ var _ xdrType = (*ScSpecEventDataFormat)(nil) // { // string doc; // string lib<80>; -// SCSymbol name; +// string name; // SCSymbol prefixTopics<2>; // SCSpecEventParamV0 params<>; // SCSpecEventDataFormat dataFormat; // }; type ScSpecEventV0 struct { - Doc string `xdrmaxsize:"1024"` - Lib string `xdrmaxsize:"80"` - Name ScSymbol + Doc string `xdrmaxsize:"1024"` + Lib string `xdrmaxsize:"80"` + Name string `xdrmaxsize:"1024"` PrefixTopics []ScSymbol `xdrmaxsize:"2"` Params []ScSpecEventParamV0 DataFormat ScSpecEventDataFormat @@ -57003,7 +57008,7 @@ func (s *ScSpecEventV0) EncodeTo(e *xdr.Encoder) error { if _, err = e.EncodeString(string(s.Lib)); err != nil { return err } - if err = s.Name.EncodeTo(e); err != nil { + if _, err = e.EncodeString(string(s.Name)); err != nil { return err } if _, err = e.EncodeUint(uint32(len(s.PrefixTopics))); err != nil { @@ -57048,10 +57053,10 @@ func (s *ScSpecEventV0) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { if err != nil { return n, fmt.Errorf("decoding Lib: %w", err) } - nTmp, err = s.Name.DecodeFrom(d, maxDepth) + s.Name, nTmp, err = d.DecodeString(1024) n += nTmp if err != nil { - return n, fmt.Errorf("decoding ScSymbol: %w", err) + return n, fmt.Errorf("decoding Name: %w", err) } var l uint32 l, nTmp, err = d.DecodeUint() @@ -59100,6 +59105,8 @@ var _ xdrType = (*ContractExecutableType)(nil) // SC_ADDRESS_TYPE_MUXED_ACCOUNT = 2, // SC_ADDRESS_TYPE_CLAIMABLE_BALANCE = 3, // SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4 +// , +// SC_ADDRESS_TYPE_MUXED_CONTRACT = 5 // }; type ScAddressType int32 @@ -59109,6 +59116,7 @@ const ( ScAddressTypeScAddressTypeMuxedAccount ScAddressType = 2 ScAddressTypeScAddressTypeClaimableBalance ScAddressType = 3 ScAddressTypeScAddressTypeLiquidityPool ScAddressType = 4 + ScAddressTypeScAddressTypeMuxedContract ScAddressType = 5 ) var scAddressTypeMap = map[int32]string{ @@ -59117,6 +59125,7 @@ var scAddressTypeMap = map[int32]string{ 2: "ScAddressTypeScAddressTypeMuxedAccount", 3: "ScAddressTypeScAddressTypeClaimableBalance", 4: "ScAddressTypeScAddressTypeLiquidityPool", + 5: "ScAddressTypeScAddressTypeMuxedContract", } // ValidEnum validates a proposed value for this enum. Implements @@ -59263,6 +59272,81 @@ func (s MuxedEd25519Account) xdrType() {} var _ xdrType = (*MuxedEd25519Account)(nil) +// MuxedContract is an XDR Struct defines as: +// +// struct MuxedContract +// { +// uint64 id; +// ContractID contractId; +// }; +type MuxedContract struct { + Id Uint64 + ContractId ContractId +} + +// EncodeTo encodes this value using the Encoder. +func (s *MuxedContract) EncodeTo(e *xdr.Encoder) error { + var err error + if err = s.Id.EncodeTo(e); err != nil { + return err + } + if err = s.ContractId.EncodeTo(e); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*MuxedContract)(nil) + +// DecodeFrom decodes this value using the Decoder. +func (s *MuxedContract) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MuxedContract: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = s.Id.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Uint64: %w", err) + } + nTmp, err = s.ContractId.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding ContractId: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MuxedContract) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MuxedContract) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MuxedContract)(nil) + _ encoding.BinaryUnmarshaler = (*MuxedContract)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s MuxedContract) xdrType() {} + +var _ xdrType = (*MuxedContract)(nil) + // ScAddress is an XDR Union defines as: // // union SCAddress switch (SCAddressType type) @@ -59277,6 +59361,8 @@ var _ xdrType = (*MuxedEd25519Account)(nil) // ClaimableBalanceID claimableBalanceId; // case SC_ADDRESS_TYPE_LIQUIDITY_POOL: // PoolID liquidityPoolId; +// case SC_ADDRESS_TYPE_MUXED_CONTRACT: +// MuxedContract muxedContract; // }; type ScAddress struct { Type ScAddressType @@ -59285,6 +59371,7 @@ type ScAddress struct { MuxedAccount *MuxedEd25519Account ClaimableBalanceId *ClaimableBalanceId LiquidityPoolId *PoolId + MuxedContract *MuxedContract } // SwitchFieldName returns the field name in which this union's @@ -59307,6 +59394,8 @@ func (u ScAddress) ArmForSwitch(sw int32) (string, bool) { return "ClaimableBalanceId", true case ScAddressTypeScAddressTypeLiquidityPool: return "LiquidityPoolId", true + case ScAddressTypeScAddressTypeMuxedContract: + return "MuxedContract", true } return "-", false } @@ -59350,6 +59439,13 @@ func NewScAddress(aType ScAddressType, value interface{}) (result ScAddress, err return } result.LiquidityPoolId = &tv + case ScAddressTypeScAddressTypeMuxedContract: + tv, ok := value.(MuxedContract) + if !ok { + err = errors.New("invalid value, must be MuxedContract") + return + } + result.MuxedContract = &tv } return } @@ -59479,6 +59575,31 @@ func (u ScAddress) GetLiquidityPoolId() (result PoolId, ok bool) { return } +// MustMuxedContract retrieves the MuxedContract value from the union, +// panicing if the value is not set. +func (u ScAddress) MustMuxedContract() MuxedContract { + val, ok := u.GetMuxedContract() + + if !ok { + panic("arm MuxedContract is not set") + } + + return val +} + +// GetMuxedContract retrieves the MuxedContract value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u ScAddress) GetMuxedContract() (result MuxedContract, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "MuxedContract" { + result = *u.MuxedContract + ok = true + } + + return +} + // EncodeTo encodes this value using the Encoder. func (u ScAddress) EncodeTo(e *xdr.Encoder) error { var err error @@ -59511,6 +59632,11 @@ func (u ScAddress) EncodeTo(e *xdr.Encoder) error { return err } return nil + case ScAddressTypeScAddressTypeMuxedContract: + if err = (*u.MuxedContract).EncodeTo(e); err != nil { + return err + } + return nil } return fmt.Errorf("Type (ScAddressType) switch value '%d' is not valid for union ScAddress", u.Type) } @@ -59586,6 +59712,17 @@ func (u *ScAddress) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { return n, fmt.Errorf("decoding PoolId: %w", err) } return n, nil + case ScAddressTypeScAddressTypeMuxedContract: + if err = xdr.TrackOutputBytesOf[MuxedContract](d); err != nil { + return n, fmt.Errorf("decoding MuxedContract: %w", err) + } + u.MuxedContract = new(MuxedContract) + nTmp, err = (*u.MuxedContract).DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding MuxedContract: %w", err) + } + return n, nil } return n, fmt.Errorf("union ScAddress has invalid Type (ScAddressType) switch value '%d'", u.Type) } diff --git a/xdr/xdr_views_generated.go b/xdr/xdr_views_generated.go index 032f3c9b1c..d658ad3bf8 100644 --- a/xdr/xdr_views_generated.go +++ b/xdr/xdr_views_generated.go @@ -8387,8 +8387,8 @@ func (v ScSpecTypeUdtNameOpaqueView) Value() ([]byte, error) { if err != nil { return nil, err } - if len(val) > 60 { - return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 60) + if len(val) > 1024 { + return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 1024) } return val, nil } @@ -9178,8 +9178,8 @@ func (v ScSpecUdtStructV0NameOpaqueView) Value() ([]byte, error) { if err != nil { return nil, err } - if len(val) > 60 { - return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 60) + if len(val) > 1024 { + return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 1024) } return val, nil } @@ -10553,8 +10553,8 @@ func (v ScSpecUdtUnionV0NameOpaqueView) Value() ([]byte, error) { if err != nil { return nil, err } - if len(val) > 60 { - return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 60) + if len(val) > 1024 { + return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 1024) } return val, nil } @@ -11377,8 +11377,8 @@ func (v ScSpecUdtEnumV0NameOpaqueView) Value() ([]byte, error) { if err != nil { return nil, err } - if len(val) > 60 { - return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 60) + if len(val) > 1024 { + return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 1024) } return val, nil } @@ -12209,8 +12209,8 @@ func (v ScSpecUdtErrorEnumV0NameOpaqueView) Value() ([]byte, error) { if err != nil { return nil, err } - if len(val) > 60 { - return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 60) + if len(val) > 1024 { + return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 1024) } return val, nil } @@ -14102,6 +14102,40 @@ func (v ScSpecEventV0LibOpaqueView) ValidateFull() error { retu func (v ScSpecEventV0LibOpaqueView) MustRaw() []byte { return must(v.Raw()) } func (v ScSpecEventV0LibOpaqueView) MustCopy() ScSpecEventV0LibOpaqueView { return must(v.Copy()) } +type ScSpecEventV0NameOpaqueView []byte + +func (v ScSpecEventV0NameOpaqueView) Value() ([]byte, error) { + val, err := VarOpaqueView(v).Value() + if err != nil { + return nil, err + } + if len(val) > 1024 { + return nil, viewErrOpaqueExceedsMax(0, uint32(len(val)), 1024) + } + return val, nil +} +func (v ScSpecEventV0NameOpaqueView) size(depth int) (int, error) { + return VarOpaqueView(v).size(depth) +} +func (v ScSpecEventV0NameOpaqueView) valid(_ int) (int, error) { + if _, err := v.Value(); err != nil { + return 0, err + } + return v.size(0) +} +func (v ScSpecEventV0NameOpaqueView) MustValue() []byte { return must(v.Value()) } + +// Raw returns the exact wire bytes for this view, trimmed from the fat slice. +func (v ScSpecEventV0NameOpaqueView) Raw() ([]byte, error) { return viewRaw(v) } + +// Copy returns an independent copy of this view that does not alias the original bytes. +func (v ScSpecEventV0NameOpaqueView) Copy() (ScSpecEventV0NameOpaqueView, error) { return viewCopy(v) } + +// ValidateFull checks that this view is well-formed: bounds, schema constraints, and depth limits. +func (v ScSpecEventV0NameOpaqueView) ValidateFull() error { return validate(v) } +func (v ScSpecEventV0NameOpaqueView) MustRaw() []byte { return must(v.Raw()) } +func (v ScSpecEventV0NameOpaqueView) MustCopy() ScSpecEventV0NameOpaqueView { return must(v.Copy()) } + type ScSpecEventV0PrefixTopicsView []byte func (v ScSpecEventV0PrefixTopicsView) Count() (int, error) { @@ -14381,7 +14415,7 @@ func (v ScSpecEventV0View) size(depth int) (int, error) { return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") } { - sz, err := ScSymbolView(v[off:]).size(depth + 1) + sz, err := ScSpecEventV0NameOpaqueView(v[off:]).size(depth + 1) if err != nil { return 0, err } @@ -14447,7 +14481,7 @@ func (v ScSpecEventV0View) Lib() (ScSpecEventV0LibOpaqueView, error) { return ScSpecEventV0LibOpaqueView(v[off:]), nil } func (v ScSpecEventV0View) MustLib() ScSpecEventV0LibOpaqueView { return must(v.Lib()) } -func (v ScSpecEventV0View) Name() (ScSymbolView, error) { +func (v ScSpecEventV0View) Name() (ScSpecEventV0NameOpaqueView, error) { off := int64(0) if off > int64(len(v)) { return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") @@ -14478,9 +14512,9 @@ func (v ScSpecEventV0View) Name() (ScSymbolView, error) { if off > int64(len(v)) { return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") } - return ScSymbolView(v[off:]), nil + return ScSpecEventV0NameOpaqueView(v[off:]), nil } -func (v ScSpecEventV0View) MustName() ScSymbolView { return must(v.Name()) } +func (v ScSpecEventV0View) MustName() ScSpecEventV0NameOpaqueView { return must(v.Name()) } func (v ScSpecEventV0View) PrefixTopics() (ScSpecEventV0PrefixTopicsView, error) { off := int64(0) if off > int64(len(v)) { @@ -14513,7 +14547,7 @@ func (v ScSpecEventV0View) PrefixTopics() (ScSpecEventV0PrefixTopicsView, error) return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") } { - sz, err := ScSymbolView(v[off:]).size(0) + sz, err := ScSpecEventV0NameOpaqueView(v[off:]).size(0) if err != nil { return nil, err } @@ -14562,7 +14596,7 @@ func (v ScSpecEventV0View) Params() (ScSpecEventV0ParamsView, error) { return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") } { - sz, err := ScSymbolView(v[off:]).size(0) + sz, err := ScSpecEventV0NameOpaqueView(v[off:]).size(0) if err != nil { return nil, err } @@ -14622,7 +14656,7 @@ func (v ScSpecEventV0View) DataFormat() (ScSpecEventDataFormatView, error) { return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") } { - sz, err := ScSymbolView(v[off:]).size(0) + sz, err := ScSpecEventV0NameOpaqueView(v[off:]).size(0) if err != nil { return nil, err } @@ -14689,7 +14723,7 @@ func (v ScSpecEventV0View) valid(depth int) (int, error) { } } { - sz, err := ScSymbolView(v[off:]).valid(depth + 1) + sz, err := ScSpecEventV0NameOpaqueView(v[off:]).valid(depth + 1) if err != nil { return 0, err } @@ -14747,7 +14781,7 @@ type ScSpecEventV0Fields struct { View ScSpecEventV0View Doc ScSpecEventV0DocOpaqueView Lib ScSpecEventV0LibOpaqueView - Name ScSymbolView + Name ScSpecEventV0NameOpaqueView PrefixTopics ScSpecEventV0PrefixTopicsView Params ScSpecEventV0ParamsView DataFormat ScSpecEventDataFormatView @@ -14790,7 +14824,7 @@ func locateScSpecEventV0(v ScSpecEventV0View) (ScSpecEventV0Fields, error) { return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") } { - sz, err := ScSymbolView(v[off:]).size(0) + sz, err := ScSpecEventV0NameOpaqueView(v[off:]).size(0) if err != nil { return f, err } @@ -14798,7 +14832,7 @@ func locateScSpecEventV0(v ScSpecEventV0View) (ScSpecEventV0Fields, error) { if off+fsz > int64(len(v)) { return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") } - f.Name = ScSymbolView(v[off : off+fsz]) + f.Name = ScSpecEventV0NameOpaqueView(v[off : off+fsz]) off += fsz } if off > int64(len(v)) { @@ -15811,7 +15845,7 @@ func (v ScAddressTypeView) Value() (ScAddressType, error) { } val := ScAddressType(int32(binary.BigEndian.Uint32(v[:4]))) switch val { - case ScAddressTypeScAddressTypeAccount, ScAddressTypeScAddressTypeContract, ScAddressTypeScAddressTypeMuxedAccount, ScAddressTypeScAddressTypeClaimableBalance, ScAddressTypeScAddressTypeLiquidityPool: + case ScAddressTypeScAddressTypeAccount, ScAddressTypeScAddressTypeContract, ScAddressTypeScAddressTypeMuxedAccount, ScAddressTypeScAddressTypeClaimableBalance, ScAddressTypeScAddressTypeLiquidityPool, ScAddressTypeScAddressTypeMuxedContract: return val, nil default: return 0, viewErrUnknownDiscriminant(0, int32(val)) @@ -15921,6 +15955,88 @@ func (v MuxedEd25519AccountView) Fields() (MuxedEd25519AccountFields, error) { return locateMuxedEd25519Account(v) } +type MuxedContractView []byte + +func (v MuxedContractView) size(_ int) (int, error) { return 40, nil } +func (v MuxedContractView) Id() (Uint64View, error) { + if len(v) < 40 { + return nil, viewErrShortBuffer(0, "need 40 bytes") + } + return Uint64View(v[0:]), nil +} +func (v MuxedContractView) MustId() Uint64View { return must(v.Id()) } +func (v MuxedContractView) ContractId() (ContractIdView, error) { + if len(v) < 40 { + return nil, viewErrShortBuffer(0, "need 40 bytes") + } + off := int64(0) + off += 8 + if off > int64(len(v)) { + return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return ContractIdView(v[off:]), nil +} +func (v MuxedContractView) MustContractId() ContractIdView { return must(v.ContractId()) } +func (v MuxedContractView) valid(depth int) (int, error) { + if len(v) < 40 { + return 0, viewErrShortBuffer(0, "need 40 bytes") + } + off := int64(0) + { + sz, err := Uint64View(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + { + sz, err := ContractIdView(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + return int(off), nil +} + +// Raw returns the exact wire bytes for this view, trimmed from the fat slice. +func (v MuxedContractView) Raw() ([]byte, error) { return viewRaw(v) } + +// Copy returns an independent copy of this view that does not alias the original bytes. +func (v MuxedContractView) Copy() (MuxedContractView, error) { return viewCopy(v) } + +// ValidateFull checks that this view is well-formed: bounds, schema constraints, and depth limits. +func (v MuxedContractView) ValidateFull() error { return validate(v) } +func (v MuxedContractView) MustRaw() []byte { return must(v.Raw()) } +func (v MuxedContractView) MustCopy() MuxedContractView { return must(v.Copy()) } + +// MuxedContractFields is the located form of MuxedContractView: every field trimmed to its exact wire extent, all found in one walk. +type MuxedContractFields struct { + View MuxedContractView + Id Uint64View + ContractId ContractIdView +} + +func locateMuxedContract(v MuxedContractView) (MuxedContractFields, error) { + var f MuxedContractFields + if len(v) < 40 { + return f, viewErrShortBuffer(0, "need 40 bytes") + } + f.Id = Uint64View(v[0:8]) + f.ContractId = ContractIdView(v[8:40]) + f.View = MuxedContractView(v[:40]) + return f, nil +} + +// Fields locates every field of this node in a single walk, each trimmed to its exact wire extent. +func (v MuxedContractView) Fields() (MuxedContractFields, error) { return locateMuxedContract(v) } + type ScAddressView []byte func (v ScAddressView) size(depth int) (int, error) { @@ -15977,6 +16093,15 @@ func (v ScAddressView) size(depth int) (int, error) { return 0, viewErrShortBuffer(4, "arm exceeds data") } return 4 + sz, nil + case int32(ScAddressTypeScAddressTypeMuxedContract): + sz, err := MuxedContractView(v[4:]).size(depth + 1) + if err != nil { + return 0, err + } + if 4+sz > len(v) { + return 0, viewErrShortBuffer(4, "arm exceeds data") + } + return 4 + sz, nil default: return 0, viewErrUnknownDiscriminant(0, disc) } @@ -15987,7 +16112,7 @@ func (v ScAddressView) Type() (ScAddressType, error) { } val := ScAddressType(int32(binary.BigEndian.Uint32(v[:4]))) switch val { - case ScAddressTypeScAddressTypeAccount, ScAddressTypeScAddressTypeContract, ScAddressTypeScAddressTypeMuxedAccount, ScAddressTypeScAddressTypeClaimableBalance, ScAddressTypeScAddressTypeLiquidityPool: + case ScAddressTypeScAddressTypeAccount, ScAddressTypeScAddressTypeContract, ScAddressTypeScAddressTypeMuxedAccount, ScAddressTypeScAddressTypeClaimableBalance, ScAddressTypeScAddressTypeLiquidityPool, ScAddressTypeScAddressTypeMuxedContract: return val, nil default: return 0, viewErrUnknownDiscriminant(0, int32(val)) @@ -16061,6 +16186,19 @@ func (v ScAddressView) LiquidityPoolId() (PoolIdView, error) { return PoolIdView(v[4:]), nil } func (v ScAddressView) MustLiquidityPoolId() PoolIdView { return must(v.LiquidityPoolId()) } +func (v ScAddressView) MuxedContract() (MuxedContractView, error) { + if len(v) < 4 { + return nil, viewErrShortBuffer(0, "need 4 bytes for discriminant") + } + disc := int32(binary.BigEndian.Uint32(v[:4])) + switch disc { + case int32(ScAddressTypeScAddressTypeMuxedContract): + default: + return nil, viewErrWrongDiscriminant(0, disc, int32(ScAddressTypeScAddressTypeMuxedContract)) + } + return MuxedContractView(v[4:]), nil +} +func (v ScAddressView) MustMuxedContract() MuxedContractView { return must(v.MuxedContract()) } func (v ScAddressView) valid(depth int) (int, error) { if depth > maxDepth { return 0, viewErrMaxDepth(0) @@ -16115,6 +16253,15 @@ func (v ScAddressView) valid(depth int) (int, error) { return 0, viewErrShortBuffer(4, "arm exceeds data") } return 4 + sz, nil + case int32(ScAddressTypeScAddressTypeMuxedContract): + sz, err := MuxedContractView(v[4:]).valid(depth + 1) + if err != nil { + return 0, err + } + if 4+sz > len(v) { + return 0, viewErrShortBuffer(4, "arm exceeds data") + } + return 4 + sz, nil default: return 0, viewErrUnknownDiscriminant(0, disc) } From 50ebbb65261592710c0aab55402f8b4f49bc9737 Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Wed, 23 Sep 2026 14:01:32 -0700 Subject: [PATCH 02/10] strkey, xdr: support muxed contract addresses (CAP-0084) Adds the W... muxed contract strkey (vectors from rs-stellar-strkey@80085faa) and handles the new ScAddress arm in String() and Equals(). --- CHANGELOG.md | 5 ++ .../token_transfer/contract_events_test.go | 31 ++++++++ strkey/decode_length_test.go | 1 + strkey/decode_test.go | 12 +++ strkey/encode_test.go | 12 +++ strkey/isvalid_length_test.go | 14 ++++ strkey/main.go | 14 +++- strkey/muxed_contract.go | 67 +++++++++++++++++ strkey/muxed_contract_test.go | 74 +++++++++++++++++++ support/contractevents/event_test.go | 7 ++ support/contractevents/generate.go | 10 +++ xdr/scval.go | 9 +++ xdr/scval_test.go | 10 +++ 13 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 strkey/muxed_contract.go create mode 100644 strkey/muxed_contract_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ff009c73..28dc2f3c94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,12 @@ This monorepo contains a number of sdk's: Official project releases may be found here: https://github.com/stellar/go-stellar-sdk/releases ## Pending +### Breaking Changes +* xdr: Regenerated from stellar-xdr@01741cb6 with `CAP_0084_MUXED_CONTRACT` enabled. Contract spec UDT and event names are widened to `SC_SPEC_TYPE_NAME_LIMIT` (1024 bytes), and `ScSpecEventV0.Name` is now a `string` instead of an `ScSymbol`. + ### New Features +* xdr: Add the CAP-0084 `ScAddressTypeScAddressTypeMuxedContract` arm of `ScAddress`. `ScAddress.String()` renders it as a `W...` muxed contract strkey. +* strkey: Add `VersionByteMuxedContract` (`W...`), `MuxedContract`, `DecodeMuxedContract`, and `IsValidMuxedContractAddress`. * xdr: Added `LedgerCloseMetaView.LedgerHeader()`, exposing the version-resolving header accessor that already backs `LedgerSequence`, `LedgerCloseTime`, `LedgerHash`, and `PreviousLedgerHash` ([#5982](https://github.com/stellar/go-stellar-sdk/pull/5982)) * rpcclient: Add `Client.URL()` to expose the configured RPC server URL ([#5885](https://github.com/stellar/go-stellar-sdk/issues/5885)) * protocols/rpc: Add getEvents v2 wire types and request validation (`GetEventsV2Request`/`GetEventsV2Response`, `EventFilterV2`, scan statuses, typed `error.data` payloads), transcribed from the [accepted proposal](https://github.com/orgs/stellar/discussions/1872) ([#5971](https://github.com/stellar/go-stellar-sdk/pull/5971)) diff --git a/processors/token_transfer/contract_events_test.go b/processors/token_transfer/contract_events_test.go index b58445309a..08a2e27edb 100644 --- a/processors/token_transfer/contract_events_test.go +++ b/processors/token_transfer/contract_events_test.go @@ -24,6 +24,8 @@ var ( someContract2 = strkey.MustEncode(strkey.VersionByteContract, someContractId2[:]) + someMuxedContract1 = strkey.MustEncode(strkey.VersionByteMuxedContract, append(someContractId1[:], 0, 0, 0, 0, 0, 0, 0, 7)) + processor = &EventsProcessor{ networkPassphrase: someNetworkPassphrase, } @@ -60,6 +62,17 @@ func createAddress(address string) xdr.ScVal { scAddress.Type = xdr.ScAddressTypeScAddressTypeAccount scAddress.AccountId = xdr.MustAddressPtr(address) + case strkey.IsValidMuxedContractAddress(address) == true: + muxed, err := strkey.DecodeMuxedContract(address) + if err != nil { + panic(err) + } + scAddress.Type = xdr.ScAddressTypeScAddressTypeMuxedContract + scAddress.MuxedContract = &xdr.MuxedContract{ + Id: xdr.Uint64(muxed.ID()), + ContractId: muxed.Contract(), + } + default: panic(fmt.Errorf("unsupported address: %s", address)) } @@ -1211,6 +1224,24 @@ func TestValidSep41EventsWithExtraTopicsAndDataV4(t *testing.T) { assert.Equal(t, thousandStr, event.GetTransfer().Amount) assert.Nil(t, event.Meta.GetToMuxedInfo()) }, + }, { + name: "Transfer Event with muxed contract address in topics - Valid Sep-41 token", + setupEvent: func() xdr.ContractEvent { + topics := []xdr.ScVal{ + createSymbol(TransferEvent), + createAddress(randomAccount), // from + createAddress(someMuxedContract1), // to + } + data := createInt128(thousand) + return createContract(&someContractId1, topics, data) + }, + validateEvent: func(t *testing.T, event *TokenTransferEvent) { + assert.NotNil(t, event.GetTransfer()) + assert.Equal(t, randomAccount, event.GetTransfer().From) + assert.Equal(t, someMuxedContract1, event.GetTransfer().To) + assert.Equal(t, thousandStr, event.GetTransfer().Amount) + assert.Nil(t, event.Meta.GetToMuxedInfo()) + }, }, { diff --git a/strkey/decode_length_test.go b/strkey/decode_length_test.go index a47fea2bfc..33449e1839 100644 --- a/strkey/decode_length_test.go +++ b/strkey/decode_length_test.go @@ -21,6 +21,7 @@ func TestDecodeRejectsNonCanonicalPayloadLength(t *testing.T) { {"liquidity_pool", VersionByteLiquidityPool, 32}, {"claimable_balance", VersionByteClaimableBalance, 33}, {"muxed_account", VersionByteMuxedAccount, 40}, + {"muxed_contract", VersionByteMuxedContract, 40}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/strkey/decode_test.go b/strkey/decode_test.go index 1736b45af3..ebee7f0db7 100644 --- a/strkey/decode_test.go +++ b/strkey/decode_test.go @@ -37,6 +37,18 @@ func TestDecode(t *testing.T) { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, + { + Name: "MuxedContract", + Address: "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IG", + ExpectedVersionByte: VersionByteMuxedContract, + ExpectedPayload: []byte{ + 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, + 0xd0, 0xf4, 0xed, 0x88, 0xc7, 0x79, 0xe4, 0xfe, + 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, + 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe2, 0x40, + }, + }, { Name: "Seed", Address: "SBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWHOKR", diff --git a/strkey/encode_test.go b/strkey/encode_test.go index a5d3a8dcac..14b31e021d 100644 --- a/strkey/encode_test.go +++ b/strkey/encode_test.go @@ -37,6 +37,18 @@ func TestEncode(t *testing.T) { }, Expected: "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", }, + { + Name: "MuxedContract", + VersionByte: VersionByteMuxedContract, + Payload: []byte{ + 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, + 0xd0, 0xf4, 0xed, 0x88, 0xc7, 0x79, 0xe4, 0xfe, + 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, + 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe2, 0x40, + }, + Expected: "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IG", + }, { Name: "Seed", VersionByte: VersionByteSeed, diff --git a/strkey/isvalid_length_test.go b/strkey/isvalid_length_test.go index 5bdd54a6b4..2043af1b3e 100644 --- a/strkey/isvalid_length_test.go +++ b/strkey/isvalid_length_test.go @@ -53,4 +53,18 @@ func TestIsValidRejectsNonCanonicalPayloadLength(t *testing.T) { require.NoError(t, err) require.True(t, strkey.IsValidMuxedAccountEd25519PublicKey(canonical)) }) + + t.Run("muxed_contract", func(t *testing.T) { + overlong, err := strkey.Encode(strkey.VersionByteMuxedContract, make([]byte, 44)) + require.NoError(t, err) + require.False(t, strkey.IsValidMuxedContractAddress(overlong)) + + short, err := strkey.Encode(strkey.VersionByteMuxedContract, make([]byte, 39)) + require.NoError(t, err) + require.False(t, strkey.IsValidMuxedContractAddress(short)) + + canonical, err := strkey.Encode(strkey.VersionByteMuxedContract, make([]byte, 40)) + require.NoError(t, err) + require.True(t, strkey.IsValidMuxedContractAddress(canonical)) + }) } diff --git a/strkey/main.go b/strkey/main.go index d8fb61730b..3862808c6e 100644 --- a/strkey/main.go +++ b/strkey/main.go @@ -47,6 +47,9 @@ const ( //VersionByteClaimableBalance is the version byte used for encoded stellar claimable balances VersionByteClaimableBalance = 1 << 3 + + //VersionByteMuxedContract is the version byte used for encoded stellar multiplexed contracts + VersionByteMuxedContract = 22 << 3 // Base32-encodes to 'W...' ) // maxPayloadSize is the maximum length of the payload for all versions. The @@ -155,6 +158,8 @@ func checkPayloadLength(version VersionByte, payload []byte) error { expected = 33 // 1-byte claimable balance type + 32-byte hash case VersionByteMuxedAccount: expected = 40 // 32-byte ed25519 key + 8-byte account id + case VersionByteMuxedContract: + expected = 40 // 32-byte contract id + 8-byte muxed id case VersionByteSignedPayload: return checkSignedPayload(payload) default: @@ -261,7 +266,8 @@ func checkValidVersionByte(version VersionByte) error { switch version { case VersionByteAccountID, VersionByteMuxedAccount, VersionByteSeed, VersionByteHashTx, VersionByteHashX, VersionByteSignedPayload, - VersionByteContract, VersionByteLiquidityPool, VersionByteClaimableBalance: + VersionByteContract, VersionByteLiquidityPool, VersionByteClaimableBalance, + VersionByteMuxedContract: return nil default: return ErrInvalidVersionByte @@ -375,6 +381,12 @@ func IsValidMuxedAccountEd25519PublicKey(s string) bool { return err == nil } +// IsValidMuxedContractAddress validates a Stellar muxed contract W-address. +func IsValidMuxedContractAddress(s string) bool { + _, err := Decode(VersionByteMuxedContract, s) + return err == nil +} + // IsValidEd25519SecretSeed validates a stellar secret key func IsValidEd25519SecretSeed(i interface{}) bool { enc, ok := i.(string) diff --git a/strkey/muxed_contract.go b/strkey/muxed_contract.go new file mode 100644 index 0000000000..49e5e37b40 --- /dev/null +++ b/strkey/muxed_contract.go @@ -0,0 +1,67 @@ +package strkey + +import ( + "encoding/binary" + + "github.com/stellar/go-stellar-sdk/support/errors" +) + +type MuxedContract struct { + id uint64 + contractID [32]byte +} + +// SetID populates the muxed contract ID. +func (m *MuxedContract) SetID(id uint64) { + m.id = id +} + +// SetContractID populates the muxed contract's underlying C-address. +func (m *MuxedContract) SetContractID(address string) error { + raw, err := Decode(VersionByteContract, address) + if err != nil { + return errors.New("invalid contract address") + } + + copy(m.contractID[:], raw) + + return nil +} + +// ID returns the muxed contract id. +func (m *MuxedContract) ID() uint64 { + return m.id +} + +// ContractID returns the muxed contract's underlying C-address. +func (m *MuxedContract) ContractID() (string, error) { + return Encode(VersionByteContract, m.contractID[:]) +} + +// Contract returns the muxed contract's raw 32-byte contract id. +func (m *MuxedContract) Contract() [32]byte { + return m.contractID +} + +// Address returns the muxed contract W-address. +func (m *MuxedContract) Address() (string, error) { + var raw [40]byte + copy(raw[:32], m.contractID[:]) + binary.BigEndian.PutUint64(raw[32:], m.id) + return Encode(VersionByteMuxedContract, raw[:]) +} + +// DecodeMuxedContract receives a muxed contract W-address and parses it into +// a MuxedContract object containing a contract id and a muxed id. +func DecodeMuxedContract(address string) (*MuxedContract, error) { + raw, err := Decode(VersionByteMuxedContract, address) + if err != nil { + return nil, errors.New("invalid muxed contract") + } + + var muxed MuxedContract + copy(muxed.contractID[:], raw[:32]) + muxed.id = binary.BigEndian.Uint64(raw[32:]) + + return &muxed, nil +} diff --git a/strkey/muxed_contract_test.go b/strkey/muxed_contract_test.go new file mode 100644 index 0000000000..2b3505d161 --- /dev/null +++ b/strkey/muxed_contract_test.go @@ -0,0 +1,74 @@ +package strkey + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Vectors generated with rs-stellar-strkey@80085faa. +var muxedContractVectors = []struct { + id uint64 + address string +}{ + {0, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAAAAAMIU"}, + {1, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAAAAE4ZU"}, + {123, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAAAPPSEK"}, + {123456, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IG"}, + {9223372036854775808, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHAAAAAAAAAAAACMXO"}, + {18446744073709551615, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQH77777777777774SY"}, +} + +const muxedContractBase = "CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE" + +func TestMuxedContract_Address(t *testing.T) { + for _, v := range muxedContractVectors { + muxed := MuxedContract{} + require.NoError(t, muxed.SetContractID(muxedContractBase)) + muxed.SetID(v.id) + address, err := muxed.Address() + require.NoError(t, err) + assert.Equal(t, v.address, address) + } + + address, err := (&MuxedContract{}).Address() + require.NoError(t, err) + assert.Equal(t, "WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCKU", address) +} + +func TestDecodeMuxedContract(t *testing.T) { + for _, v := range muxedContractVectors { + muxed, err := DecodeMuxedContract(v.address) + require.NoError(t, err) + assert.Equal(t, v.id, muxed.ID()) + contractID, err := muxed.ContractID() + require.NoError(t, err) + assert.Equal(t, muxedContractBase, contractID) + contract := muxed.Contract() + assert.Equal(t, MustDecode(VersionByteContract, muxedContractBase), contract[:]) + assert.True(t, IsValidMuxedContractAddress(v.address)) + } + + for _, invalid := range []string{ + muxedContractBase, + "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", + // bad checksum + "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IH", + // truncated + "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6", + "", + } { + muxed, err := DecodeMuxedContract(invalid) + assert.EqualError(t, err, "invalid muxed contract", invalid) + assert.Nil(t, muxed) + assert.False(t, IsValidMuxedContractAddress(invalid), invalid) + } +} + +func TestMuxedContract_SetContractID(t *testing.T) { + muxed := MuxedContract{} + assert.EqualError(t, muxed.SetContractID(""), "invalid contract address") + assert.EqualError(t, muxed.SetContractID("GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5"), "invalid contract address") + assert.Equal(t, MuxedContract{}, muxed) +} diff --git a/support/contractevents/event_test.go b/support/contractevents/event_test.go index fe4f8a384e..f9358ec3ab 100644 --- a/support/contractevents/event_test.go +++ b/support/contractevents/event_test.go @@ -51,6 +51,13 @@ func TestScValCreators(t *testing.T) { assert.True(t, ok) assert.EqualValues(t, 4, amt.Hi) assert.EqualValues(t, 1234, amt.Lo) + + muxedContract := "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IG" + val = makeAddress(muxedContract) + assert.Equal(t, xdr.ScAddressTypeScAddressTypeMuxedContract, val.MustAddress().Type) + str, err := val.MustAddress().String() + require.NoError(t, err) + assert.Equal(t, muxedContract, str) } func TestEventGenerator(t *testing.T) { diff --git a/support/contractevents/generate.go b/support/contractevents/generate.go index fed898359d..3969f8a348 100644 --- a/support/contractevents/generate.go +++ b/support/contractevents/generate.go @@ -158,6 +158,16 @@ func makeAddress(address string) xdr.ScVal { Id: xdr.Uint64(acct.ID()), Ed25519: acct.Ed25519(), } + case 'W': + muxed, err := strkey.DecodeMuxedContract(address) + if err != nil { + panic(fmt.Errorf("address is not a valid muxed contract: %s", address)) + } + scAddress.Type = xdr.ScAddressTypeScAddressTypeMuxedContract + scAddress.MuxedContract = &xdr.MuxedContract{ + Id: xdr.Uint64(muxed.ID()), + ContractId: muxed.Contract(), + } case 'L': scAddress.Type = xdr.ScAddressTypeScAddressTypeLiquidityPool scAddress.LiquidityPoolId = &xdr.PoolId{} diff --git a/xdr/scval.go b/xdr/scval.go index 38c9b336b4..c4c26373f1 100644 --- a/xdr/scval.go +++ b/xdr/scval.go @@ -2,6 +2,7 @@ package xdr import ( "bytes" + "encoding/binary" "encoding/hex" "fmt" "math/big" @@ -31,6 +32,12 @@ func (address ScAddress) String() (string, error) { }, } result, err = muxed.GetAddress() + case ScAddressTypeScAddressTypeMuxedContract: + payload := address.MustMuxedContract() + var raw [40]byte + copy(raw[:32], payload.ContractId[:]) + binary.BigEndian.PutUint64(raw[32:], uint64(payload.Id)) + result, err = strkey.Encode(strkey.VersionByteMuxedContract, raw[:]) case ScAddressTypeScAddressTypeLiquidityPool: poolID := address.MustLiquidityPoolId() result, err = strkey.Encode(strkey.VersionByteLiquidityPool, poolID[:]) @@ -163,6 +170,8 @@ func (s ScAddress) Equals(o ScAddress) bool { case ScAddressTypeScAddressTypeMuxedAccount: return s.MustMuxedAccount().Id == o.MustMuxedAccount().Id && s.MustMuxedAccount().Ed25519.Equals(o.MustMuxedAccount().Ed25519) + case ScAddressTypeScAddressTypeMuxedContract: + return s.MustMuxedContract() == o.MustMuxedContract() default: panic("unknown ScAddress type: " + s.Type.String()) } diff --git a/xdr/scval_test.go b/xdr/scval_test.go index 70bc8136ce..bc7291c6d3 100644 --- a/xdr/scval_test.go +++ b/xdr/scval_test.go @@ -93,6 +93,16 @@ func TestScAddressString(t *testing.T) { }, expected: strkey.MustEncode(strkey.VersionByteContract, contractID[:]), }, + { + address: ScAddress{ + Type: ScAddressTypeScAddressTypeMuxedContract, + MuxedContract: &MuxedContract{ + Id: 123456, + ContractId: ContractId(strkey.MustDecode(strkey.VersionByteContract, "CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE")), + }, + }, + expected: "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IG", + }, { address: ScAddress{ Type: ScAddressTypeScAddressTypeClaimableBalance, From 73e446de8da9aff153c23f65522cf2732fd9ea20 Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Mon, 28 Sep 2026 17:15:44 -0700 Subject: [PATCH 03/10] xdr: regenerate from stellar-xdr@ee040cd6 Match stellar-core #5472's protocol-curr/xdr. Only CAP-0087 ifdef'd cost types differ from 01741cb6, and they stay disabled. --- CHANGELOG.md | 2 +- Makefile | 6 +++--- xdr/xdr_commit_generated.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e438bf6b3..76a7467415 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ Official project releases may be found here: https://github.com/stellar/go-stell ## Pending ### Breaking Changes -* xdr: Regenerated from stellar-xdr@01741cb6 with `CAP_0084_MUXED_CONTRACT` enabled. Contract spec UDT and event names are widened to `SC_SPEC_TYPE_NAME_LIMIT` (1024 bytes), and `ScSpecEventV0.Name` is now a `string` instead of an `ScSymbol`. +* xdr: Regenerated from stellar-xdr@ee040cd6 with `CAP_0084_MUXED_CONTRACT` enabled. Contract spec UDT and event names are widened to `SC_SPEC_TYPE_NAME_LIMIT` (1024 bytes), and `ScSpecEventV0.Name` is now a `string` instead of an `ScSymbol`. ### New Features * xdr: Add the CAP-0084 `ScAddressTypeScAddressTypeMuxedContract` arm of `ScAddress`. `ScAddress.String()` renders it as a `W...` muxed contract strkey. diff --git a/Makefile b/Makefile index c27313d4b9..65078340aa 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,9 @@ xdr/Stellar-exporter.x XDRGEN_COMMIT=b423e1da9504239fb3136cbcc5f9beeb37795837 -# Protocol 30: stellar-xdr@01741cb6, the commit stellar-core and rs-stellar-xdr pin -# for CAP-0084 (gated behind CAP_0084_MUXED_CONTRACT, see XDR_FEATURES below). -XDR_COMMIT=01741cb652724526d3f90ffe39b417d6a88ee819 +# Protocol 30: stellar-xdr@ee040cd6, the commit stellar-core pins as src/protocol-curr/xdr +# (CAP-0084 gated behind CAP_0084_MUXED_CONTRACT, see XDR_FEATURES below). +XDR_COMMIT=ee040cd65310cd4f66d41fc74ebbbb3b6604dcc6 .PHONY: xdr xdr-clean xdr-update diff --git a/xdr/xdr_commit_generated.txt b/xdr/xdr_commit_generated.txt index d4bdb9c937..f84526d1fb 100644 --- a/xdr/xdr_commit_generated.txt +++ b/xdr/xdr_commit_generated.txt @@ -1 +1 @@ -01741cb652724526d3f90ffe39b417d6a88ee819 \ No newline at end of file +ee040cd65310cd4f66d41fc74ebbbb3b6604dcc6 \ No newline at end of file From 25819d0ea24b180098f94f6b45624356364cf53b Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Tue, 29 Sep 2026 14:27:50 -0700 Subject: [PATCH 04/10] token_transfer: skip muxed contract holders in balance deltas Contract balances aren't derivable from contractData, so C... holders were already skipped; W... addresses are contracts too. --- processors/token_transfer/verify_events.go | 2 +- processors/token_transfer/verify_events_test.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/processors/token_transfer/verify_events.go b/processors/token_transfer/verify_events.go index 7e57759289..51279ab2ec 100644 --- a/processors/token_transfer/verify_events.go +++ b/processors/token_transfer/verify_events.go @@ -20,7 +20,7 @@ type balanceKey struct { // updateBalanceMap updates the map and removes the entry if the value becomes 0 func updateBalanceMap(m map[balanceKey]*big.Int, key balanceKey, delta *big.Int) { // We dont include movement to/from contract address is balance delta tracking, since there is no standard way to derive/verify from contractData - if strkey.IsValidContractAddress(key.holder) { + if strkey.IsValidContractAddress(key.holder) || strkey.IsValidMuxedContractAddress(key.holder) { return } if delta.Sign() == 0 { diff --git a/processors/token_transfer/verify_events_test.go b/processors/token_transfer/verify_events_test.go index f6ca3b3787..2aecbd5ac4 100644 --- a/processors/token_transfer/verify_events_test.go +++ b/processors/token_transfer/verify_events_test.go @@ -28,6 +28,23 @@ func TestFindBalanceDeltasFromEvents_Int64Amounts(t *testing.T) { assert.Equal(t, big.NewInt(1000), deltas[toKey]) } +func TestFindBalanceDeltasFromEvents_SkipsContractHolders(t *testing.T) { + from := accountA.Address() + asset := xlmAsset + protoAsset := assetProto.NewProtoAsset(asset) + + meta := &EventMeta{TxHash: "abc123"} + toContract := NewTransferEvent(meta, from, someContract1, "1000", protoAsset) + toMuxedContract := NewTransferEvent(meta, from, someMuxedContract1, "500", protoAsset) + + deltas, err := findBalanceDeltasFromEvents([]*TokenTransferEvent{toContract, toMuxedContract}) + require.NoError(t, err) + + fromKey := balanceKey{holder: from, asset: asset.StringCanonical()} + assert.Equal(t, big.NewInt(-1500), deltas[fromKey]) + assert.Len(t, deltas, 1) +} + func TestFindBalanceDeltasFromEvents_AmountExceedingInt64(t *testing.T) { // This is the scenario from issue #5929: a SAC token balance that // exceeded int64 max through cumulative mints, then was fully burned. From 70bad4ef99aeed143b4d7b704c562055b71cace1 Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Mon, 5 Oct 2026 15:20:37 -0700 Subject: [PATCH 05/10] effects: add contract_muxed fields to ContractCredited A CAP-0084 muxed contract destination is recorded on contract_credited the way account_muxed/account_muxed_id record a muxed account. --- protocols/horizon/effects/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocols/horizon/effects/main.go b/protocols/horizon/effects/main.go index 4fcb97da36..55f573efde 100644 --- a/protocols/horizon/effects/main.go +++ b/protocols/horizon/effects/main.go @@ -317,7 +317,10 @@ type ContractCredited struct { Base base.Asset Contract string `json:"contract"` - Amount string `json:"amount"` + // Set when the destination was a muxed contract (CAP-0084). + ContractMuxed string `json:"contract_muxed,omitempty"` + ContractMuxedID uint64 `json:"contract_muxed_id,omitempty,string"` + Amount string `json:"amount"` } type ContractDebited struct { From 10bc40c15064311925dc91447b79d61453114170 Mon Sep 17 00:00:00 2001 From: tamirms Date: Thu, 8 Oct 2026 10:43:40 +0100 Subject: [PATCH 06/10] xdr: regenerate from stellar-xdr@4f524bba for Protocol 30 stellar-xdr main ungates CAP-0084, CAP-0087 and CAP-0088 (#319, #320, #321), so XDR_FEATURES is now empty. This brings in the CAP-0088 millisecond close-time StellarValue arms, without which no ledger after the P30 upgrade decodes, and the CAP-0087 ML-DSA cost types. Co-Authored-By: Claude Opus 5.5 --- Makefile | 7 +- gxdr/xdr_generated.go | 194 +++++++++++- xdr/Stellar-contract-config-setting.x | 20 +- xdr/Stellar-contract.x | 3 +- xdr/Stellar-ledger.x | 19 +- xdr/Stellar-types.x | 2 + xdr/xdr_commit_generated.txt | 2 +- xdr/xdr_generated.go | 435 +++++++++++++++++++++++++- xdr/xdr_views_generated.go | 417 +++++++++++++++++++++++- 9 files changed, 1063 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index 65078340aa..a443e80b6f 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,8 @@ xdr/Stellar-exporter.x XDRGEN_COMMIT=b423e1da9504239fb3136cbcc5f9beeb37795837 -# Protocol 30: stellar-xdr@ee040cd6, the commit stellar-core pins as src/protocol-curr/xdr -# (CAP-0084 gated behind CAP_0084_MUXED_CONTRACT, see XDR_FEATURES below). -XDR_COMMIT=ee040cd65310cd4f66d41fc74ebbbb3b6604dcc6 +# Protocol 30: stellar-xdr@4f524bba (main), with CAP-0084, CAP-0087 and CAP-0088 ungated upstream. +XDR_COMMIT=4f524bbac80c781c06e4fb5fc93a2d0d331d6705 .PHONY: xdr xdr-clean xdr-update @@ -69,7 +68,7 @@ xdr/xdr_generated.go: $(XDRS) # Optional comma-separated features for #ifdef resolution in the XDR files. # Empty = no features enabled; only unconditional definitions are emitted. -XDR_FEATURES ?= CAP_0084_MUXED_CONTRACT +XDR_FEATURES ?= # Generates xdr/xdr_views_generated.go via a two-step pipeline: # 1. The rust `generator-definitions-json` tool parses the .x files and diff --git a/gxdr/xdr_generated.go b/gxdr/xdr_generated.go index be6cbe867a..d59a79c57c 100644 --- a/gxdr/xdr_generated.go +++ b/gxdr/xdr_generated.go @@ -875,9 +875,11 @@ type UpgradeType = []byte // bound 128 type StellarValueType int32 const ( - STELLAR_VALUE_BASIC StellarValueType = 0 - STELLAR_VALUE_SIGNED StellarValueType = 1 - STELLAR_VALUE_EMPTY_TX_SET StellarValueType = 2 + STELLAR_VALUE_BASIC StellarValueType = 0 + STELLAR_VALUE_SIGNED StellarValueType = 1 + STELLAR_VALUE_EMPTY_TX_SET StellarValueType = 2 + STELLAR_VALUE_SIGNED_MS StellarValueType = 3 + STELLAR_VALUE_EMPTY_TX_SET_MS StellarValueType = 4 ) type LedgerCloseValueSignature struct { @@ -912,6 +914,10 @@ type XdrAnon_StellarValue_Ext struct { // LcValueSignature() *LedgerCloseValueSignature // STELLAR_VALUE_EMPTY_TX_SET: // ProposedValue() *XdrAnon_StellarValue_Ext_ProposedValue + // STELLAR_VALUE_SIGNED_MS: + // SignedMsValue() *XdrAnon_StellarValue_Ext_SignedMsValue + // STELLAR_VALUE_EMPTY_TX_SET_MS: + // ProposedMsValue() *XdrAnon_StellarValue_Ext_ProposedMsValue V StellarValueType _u interface{} } @@ -921,6 +927,19 @@ type XdrAnon_StellarValue_Ext_ProposedValue struct { PreviousLedgerVersion Uint32 LcValueSignature LedgerCloseValueSignature } +type XdrAnon_StellarValue_Ext_SignedMsValue struct { + // closeTime == closeTimeMs / 1000 + CloseTimeMs TimePointMs + LcValueSignature LedgerCloseValueSignature +} +type XdrAnon_StellarValue_Ext_ProposedMsValue struct { + // closeTime == closeTimeMs / 1000 + CloseTimeMs TimePointMs + TxSetHash Hash + PreviousLedgerHash Hash + PreviousLedgerVersion Uint32 + LcValueSignature LedgerCloseValueSignature +} const MASK_LEDGER_HEADER_FLAGS = 0x7 @@ -3902,6 +3921,9 @@ type TimePoint = Uint64 type Duration = Uint64 +// Milliseconds since the Unix epoch. TimePoint is whole seconds. +type TimePointMs = Uint64 + // An ExtensionPoint is always marshaled as a 32-bit 0 value. At a // later point, it can be replaced by a different union so as to // extend a structure. @@ -4915,6 +4937,24 @@ const ( Bn254FrInv ContractCostType = 84 // Cost of performing BN254 G1 multi-scalar multiplication (MSM) Bn254G1Msm ContractCostType = 85 + // Cost of decoding and expanding an ML-DSA-44 verifying key + MlDsa44DecodeVerifyingKey ContractCostType = 86 + // Cost of decoding and expanding an ML-DSA-65 verifying key + MlDsa65DecodeVerifyingKey ContractCostType = 87 + // Cost of decoding and expanding an ML-DSA-87 verifying key + MlDsa87DecodeVerifyingKey ContractCostType = 88 + // Cost of decoding an ML-DSA-44 signature + MlDsa44DecodeSignature ContractCostType = 89 + // Cost of decoding an ML-DSA-65 signature + MlDsa65DecodeSignature ContractCostType = 90 + // Cost of decoding an ML-DSA-87 signature + MlDsa87DecodeSignature ContractCostType = 91 + // Cost of verifying an ML-DSA-44 signature, linear in message + context length + VerifyMlDsa44Sig ContractCostType = 92 + // Cost of verifying an ML-DSA-65 signature, linear in message + context length + VerifyMlDsa65Sig ContractCostType = 93 + // Cost of verifying an ML-DSA-87 signature, linear in message + context length + VerifyMlDsa87Sig ContractCostType = 94 ) type ContractCostParamEntry struct { @@ -10048,14 +10088,18 @@ func (XdrType_UpgradeType) XdrTypeName() string { return "UpgradeType" } func (v XdrType_UpgradeType) XdrUnwrap() XdrType { return v.XdrVecOpaque } var _XdrNames_StellarValueType = map[int32]string{ - int32(STELLAR_VALUE_BASIC): "STELLAR_VALUE_BASIC", - int32(STELLAR_VALUE_SIGNED): "STELLAR_VALUE_SIGNED", - int32(STELLAR_VALUE_EMPTY_TX_SET): "STELLAR_VALUE_EMPTY_TX_SET", + int32(STELLAR_VALUE_BASIC): "STELLAR_VALUE_BASIC", + int32(STELLAR_VALUE_SIGNED): "STELLAR_VALUE_SIGNED", + int32(STELLAR_VALUE_EMPTY_TX_SET): "STELLAR_VALUE_EMPTY_TX_SET", + int32(STELLAR_VALUE_SIGNED_MS): "STELLAR_VALUE_SIGNED_MS", + int32(STELLAR_VALUE_EMPTY_TX_SET_MS): "STELLAR_VALUE_EMPTY_TX_SET_MS", } var _XdrValues_StellarValueType = map[string]int32{ - "STELLAR_VALUE_BASIC": int32(STELLAR_VALUE_BASIC), - "STELLAR_VALUE_SIGNED": int32(STELLAR_VALUE_SIGNED), - "STELLAR_VALUE_EMPTY_TX_SET": int32(STELLAR_VALUE_EMPTY_TX_SET), + "STELLAR_VALUE_BASIC": int32(STELLAR_VALUE_BASIC), + "STELLAR_VALUE_SIGNED": int32(STELLAR_VALUE_SIGNED), + "STELLAR_VALUE_EMPTY_TX_SET": int32(STELLAR_VALUE_EMPTY_TX_SET), + "STELLAR_VALUE_SIGNED_MS": int32(STELLAR_VALUE_SIGNED_MS), + "STELLAR_VALUE_EMPTY_TX_SET_MS": int32(STELLAR_VALUE_EMPTY_TX_SET_MS), } func (StellarValueType) XdrEnumNames() map[int32]string { @@ -10130,10 +10174,53 @@ func XDR_XdrAnon_StellarValue_Ext_ProposedValue(v *XdrAnon_StellarValue_Ext_Prop return v } +type XdrType_XdrAnon_StellarValue_Ext_SignedMsValue = *XdrAnon_StellarValue_Ext_SignedMsValue + +func (v *XdrAnon_StellarValue_Ext_SignedMsValue) XdrPointer() interface{} { return v } +func (XdrAnon_StellarValue_Ext_SignedMsValue) XdrTypeName() string { + return "XdrAnon_StellarValue_Ext_SignedMsValue" +} +func (v XdrAnon_StellarValue_Ext_SignedMsValue) XdrValue() interface{} { return v } +func (v *XdrAnon_StellarValue_Ext_SignedMsValue) XdrMarshal(x XDR, name string) { x.Marshal(name, v) } +func (v *XdrAnon_StellarValue_Ext_SignedMsValue) XdrRecurse(x XDR, name string) { + if name != "" { + name = x.Sprintf("%s.", name) + } + x.Marshal(x.Sprintf("%scloseTimeMs", name), XDR_TimePointMs(&v.CloseTimeMs)) + x.Marshal(x.Sprintf("%slcValueSignature", name), XDR_LedgerCloseValueSignature(&v.LcValueSignature)) +} +func XDR_XdrAnon_StellarValue_Ext_SignedMsValue(v *XdrAnon_StellarValue_Ext_SignedMsValue) *XdrAnon_StellarValue_Ext_SignedMsValue { + return v +} + +type XdrType_XdrAnon_StellarValue_Ext_ProposedMsValue = *XdrAnon_StellarValue_Ext_ProposedMsValue + +func (v *XdrAnon_StellarValue_Ext_ProposedMsValue) XdrPointer() interface{} { return v } +func (XdrAnon_StellarValue_Ext_ProposedMsValue) XdrTypeName() string { + return "XdrAnon_StellarValue_Ext_ProposedMsValue" +} +func (v XdrAnon_StellarValue_Ext_ProposedMsValue) XdrValue() interface{} { return v } +func (v *XdrAnon_StellarValue_Ext_ProposedMsValue) XdrMarshal(x XDR, name string) { x.Marshal(name, v) } +func (v *XdrAnon_StellarValue_Ext_ProposedMsValue) XdrRecurse(x XDR, name string) { + if name != "" { + name = x.Sprintf("%s.", name) + } + x.Marshal(x.Sprintf("%scloseTimeMs", name), XDR_TimePointMs(&v.CloseTimeMs)) + x.Marshal(x.Sprintf("%stxSetHash", name), XDR_Hash(&v.TxSetHash)) + x.Marshal(x.Sprintf("%spreviousLedgerHash", name), XDR_Hash(&v.PreviousLedgerHash)) + x.Marshal(x.Sprintf("%spreviousLedgerVersion", name), XDR_Uint32(&v.PreviousLedgerVersion)) + x.Marshal(x.Sprintf("%slcValueSignature", name), XDR_LedgerCloseValueSignature(&v.LcValueSignature)) +} +func XDR_XdrAnon_StellarValue_Ext_ProposedMsValue(v *XdrAnon_StellarValue_Ext_ProposedMsValue) *XdrAnon_StellarValue_Ext_ProposedMsValue { + return v +} + var _XdrTags_XdrAnon_StellarValue_Ext = map[int32]bool{ - XdrToI32(STELLAR_VALUE_BASIC): true, - XdrToI32(STELLAR_VALUE_SIGNED): true, - XdrToI32(STELLAR_VALUE_EMPTY_TX_SET): true, + XdrToI32(STELLAR_VALUE_BASIC): true, + XdrToI32(STELLAR_VALUE_SIGNED): true, + XdrToI32(STELLAR_VALUE_EMPTY_TX_SET): true, + XdrToI32(STELLAR_VALUE_SIGNED_MS): true, + XdrToI32(STELLAR_VALUE_EMPTY_TX_SET_MS): true, } func (_ XdrAnon_StellarValue_Ext) XdrValidTags() map[int32]bool { @@ -10169,9 +10256,39 @@ func (u *XdrAnon_StellarValue_Ext) ProposedValue() *XdrAnon_StellarValue_Ext_Pro return nil } } +func (u *XdrAnon_StellarValue_Ext) SignedMsValue() *XdrAnon_StellarValue_Ext_SignedMsValue { + switch u.V { + case STELLAR_VALUE_SIGNED_MS: + if v, ok := u._u.(*XdrAnon_StellarValue_Ext_SignedMsValue); ok { + return v + } else { + var zero XdrAnon_StellarValue_Ext_SignedMsValue + u._u = &zero + return &zero + } + default: + XdrPanic("XdrAnon_StellarValue_Ext.SignedMsValue accessed when V == %v", u.V) + return nil + } +} +func (u *XdrAnon_StellarValue_Ext) ProposedMsValue() *XdrAnon_StellarValue_Ext_ProposedMsValue { + switch u.V { + case STELLAR_VALUE_EMPTY_TX_SET_MS: + if v, ok := u._u.(*XdrAnon_StellarValue_Ext_ProposedMsValue); ok { + return v + } else { + var zero XdrAnon_StellarValue_Ext_ProposedMsValue + u._u = &zero + return &zero + } + default: + XdrPanic("XdrAnon_StellarValue_Ext.ProposedMsValue accessed when V == %v", u.V) + return nil + } +} func (u XdrAnon_StellarValue_Ext) XdrValid() bool { switch u.V { - case STELLAR_VALUE_BASIC, STELLAR_VALUE_SIGNED, STELLAR_VALUE_EMPTY_TX_SET: + case STELLAR_VALUE_BASIC, STELLAR_VALUE_SIGNED, STELLAR_VALUE_EMPTY_TX_SET, STELLAR_VALUE_SIGNED_MS, STELLAR_VALUE_EMPTY_TX_SET_MS: return true } return false @@ -10190,6 +10307,10 @@ func (u *XdrAnon_StellarValue_Ext) XdrUnionBody() XdrType { return XDR_LedgerCloseValueSignature(u.LcValueSignature()) case STELLAR_VALUE_EMPTY_TX_SET: return XDR_XdrAnon_StellarValue_Ext_ProposedValue(u.ProposedValue()) + case STELLAR_VALUE_SIGNED_MS: + return XDR_XdrAnon_StellarValue_Ext_SignedMsValue(u.SignedMsValue()) + case STELLAR_VALUE_EMPTY_TX_SET_MS: + return XDR_XdrAnon_StellarValue_Ext_ProposedMsValue(u.ProposedMsValue()) } return nil } @@ -10201,6 +10322,10 @@ func (u *XdrAnon_StellarValue_Ext) XdrUnionBodyName() string { return "LcValueSignature" case STELLAR_VALUE_EMPTY_TX_SET: return "ProposedValue" + case STELLAR_VALUE_SIGNED_MS: + return "SignedMsValue" + case STELLAR_VALUE_EMPTY_TX_SET_MS: + return "ProposedMsValue" } return "" } @@ -10225,6 +10350,12 @@ func (u *XdrAnon_StellarValue_Ext) XdrRecurse(x XDR, name string) { case STELLAR_VALUE_EMPTY_TX_SET: x.Marshal(x.Sprintf("%sproposedValue", name), XDR_XdrAnon_StellarValue_Ext_ProposedValue(u.ProposedValue())) return + case STELLAR_VALUE_SIGNED_MS: + x.Marshal(x.Sprintf("%ssignedMsValue", name), XDR_XdrAnon_StellarValue_Ext_SignedMsValue(u.SignedMsValue())) + return + case STELLAR_VALUE_EMPTY_TX_SET_MS: + x.Marshal(x.Sprintf("%sproposedMsValue", name), XDR_XdrAnon_StellarValue_Ext_ProposedMsValue(u.ProposedMsValue())) + return } XdrPanic("invalid V (%v) in XdrAnon_StellarValue_Ext", u.V) } @@ -26859,6 +26990,16 @@ func XDR_Duration(v *Duration) XdrType_Duration { func (XdrType_Duration) XdrTypeName() string { return "Duration" } func (v XdrType_Duration) XdrUnwrap() XdrType { return v.XdrType_Uint64 } +type XdrType_TimePointMs struct { + XdrType_Uint64 +} + +func XDR_TimePointMs(v *TimePointMs) XdrType_TimePointMs { + return XdrType_TimePointMs{XDR_Uint64(v)} +} +func (XdrType_TimePointMs) XdrTypeName() string { return "TimePointMs" } +func (v XdrType_TimePointMs) XdrUnwrap() XdrType { return v.XdrType_Uint64 } + var _XdrTags_ExtensionPoint = map[int32]bool{ XdrToI32(0): true, } @@ -31842,6 +31983,15 @@ var _XdrNames_ContractCostType = map[int32]string{ int32(Bn254FrPow): "Bn254FrPow", int32(Bn254FrInv): "Bn254FrInv", int32(Bn254G1Msm): "Bn254G1Msm", + int32(MlDsa44DecodeVerifyingKey): "MlDsa44DecodeVerifyingKey", + int32(MlDsa65DecodeVerifyingKey): "MlDsa65DecodeVerifyingKey", + int32(MlDsa87DecodeVerifyingKey): "MlDsa87DecodeVerifyingKey", + int32(MlDsa44DecodeSignature): "MlDsa44DecodeSignature", + int32(MlDsa65DecodeSignature): "MlDsa65DecodeSignature", + int32(MlDsa87DecodeSignature): "MlDsa87DecodeSignature", + int32(VerifyMlDsa44Sig): "VerifyMlDsa44Sig", + int32(VerifyMlDsa65Sig): "VerifyMlDsa65Sig", + int32(VerifyMlDsa87Sig): "VerifyMlDsa87Sig", } var _XdrValues_ContractCostType = map[string]int32{ "WasmInsnExec": int32(WasmInsnExec), @@ -31930,6 +32080,15 @@ var _XdrValues_ContractCostType = map[string]int32{ "Bn254FrPow": int32(Bn254FrPow), "Bn254FrInv": int32(Bn254FrInv), "Bn254G1Msm": int32(Bn254G1Msm), + "MlDsa44DecodeVerifyingKey": int32(MlDsa44DecodeVerifyingKey), + "MlDsa65DecodeVerifyingKey": int32(MlDsa65DecodeVerifyingKey), + "MlDsa87DecodeVerifyingKey": int32(MlDsa87DecodeVerifyingKey), + "MlDsa44DecodeSignature": int32(MlDsa44DecodeSignature), + "MlDsa65DecodeSignature": int32(MlDsa65DecodeSignature), + "MlDsa87DecodeSignature": int32(MlDsa87DecodeSignature), + "VerifyMlDsa44Sig": int32(VerifyMlDsa44Sig), + "VerifyMlDsa65Sig": int32(VerifyMlDsa65Sig), + "VerifyMlDsa87Sig": int32(VerifyMlDsa87Sig), } func (ContractCostType) XdrEnumNames() map[int32]string { @@ -32055,6 +32214,15 @@ var _XdrComments_ContractCostType = map[int32]string{ int32(Bn254FrPow): "Cost of performing BN254 scalar element exponentiation", int32(Bn254FrInv): "Cost of performing BN254 scalar element inversion", int32(Bn254G1Msm): "Cost of performing BN254 G1 multi-scalar multiplication (MSM)", + int32(MlDsa44DecodeVerifyingKey): "Cost of decoding and expanding an ML-DSA-44 verifying key", + int32(MlDsa65DecodeVerifyingKey): "Cost of decoding and expanding an ML-DSA-65 verifying key", + int32(MlDsa87DecodeVerifyingKey): "Cost of decoding and expanding an ML-DSA-87 verifying key", + int32(MlDsa44DecodeSignature): "Cost of decoding an ML-DSA-44 signature", + int32(MlDsa65DecodeSignature): "Cost of decoding an ML-DSA-65 signature", + int32(MlDsa87DecodeSignature): "Cost of decoding an ML-DSA-87 signature", + int32(VerifyMlDsa44Sig): "Cost of verifying an ML-DSA-44 signature, linear in message + context length", + int32(VerifyMlDsa65Sig): "Cost of verifying an ML-DSA-65 signature, linear in message + context length", + int32(VerifyMlDsa87Sig): "Cost of verifying an ML-DSA-87 signature, linear in message + context length", } func (e ContractCostType) XdrEnumComments() map[int32]string { diff --git a/xdr/Stellar-contract-config-setting.x b/xdr/Stellar-contract-config-setting.x index 80c0d7fcf2..bab260a983 100644 --- a/xdr/Stellar-contract-config-setting.x +++ b/xdr/Stellar-contract-config-setting.x @@ -296,7 +296,25 @@ enum ContractCostType { // Cost of performing BN254 scalar element inversion Bn254FrInv = 84, // Cost of performing BN254 G1 multi-scalar multiplication (MSM) - Bn254G1Msm = 85 + Bn254G1Msm = 85, + // Cost of decoding and expanding an ML-DSA-44 verifying key + MlDsa44DecodeVerifyingKey = 86, + // Cost of decoding and expanding an ML-DSA-65 verifying key + MlDsa65DecodeVerifyingKey = 87, + // Cost of decoding and expanding an ML-DSA-87 verifying key + MlDsa87DecodeVerifyingKey = 88, + // Cost of decoding an ML-DSA-44 signature + MlDsa44DecodeSignature = 89, + // Cost of decoding an ML-DSA-65 signature + MlDsa65DecodeSignature = 90, + // Cost of decoding an ML-DSA-87 signature + MlDsa87DecodeSignature = 91, + // Cost of verifying an ML-DSA-44 signature, linear in message + context length + VerifyMlDsa44Sig = 92, + // Cost of verifying an ML-DSA-65 signature, linear in message + context length + VerifyMlDsa65Sig = 93, + // Cost of verifying an ML-DSA-87 signature, linear in message + context length + VerifyMlDsa87Sig = 94 }; struct ContractCostParamEntry { diff --git a/xdr/Stellar-contract.x b/xdr/Stellar-contract.x index a891fd0375..ad04b1bc39 100644 --- a/xdr/Stellar-contract.x +++ b/xdr/Stellar-contract.x @@ -182,8 +182,7 @@ enum SCAddressType SC_ADDRESS_TYPE_CONTRACT = 1, SC_ADDRESS_TYPE_MUXED_ACCOUNT = 2, SC_ADDRESS_TYPE_CLAIMABLE_BALANCE = 3, - SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4 - , + SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4, SC_ADDRESS_TYPE_MUXED_CONTRACT = 5 }; diff --git a/xdr/Stellar-ledger.x b/xdr/Stellar-ledger.x index 9acf1d773c..47726e3fa5 100644 --- a/xdr/Stellar-ledger.x +++ b/xdr/Stellar-ledger.x @@ -14,7 +14,9 @@ enum StellarValueType { STELLAR_VALUE_BASIC = 0, STELLAR_VALUE_SIGNED = 1, - STELLAR_VALUE_EMPTY_TX_SET = 2 + STELLAR_VALUE_EMPTY_TX_SET = 2, + STELLAR_VALUE_SIGNED_MS = 3, + STELLAR_VALUE_EMPTY_TX_SET_MS = 4 }; struct LedgerCloseValueSignature @@ -52,6 +54,21 @@ struct StellarValue uint32 previousLedgerVersion; LedgerCloseValueSignature lcValueSignature; } proposedValue; + case STELLAR_VALUE_SIGNED_MS: + struct + { + TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 + LedgerCloseValueSignature lcValueSignature; + } signedMsValue; + case STELLAR_VALUE_EMPTY_TX_SET_MS: + struct + { + TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 + Hash txSetHash; + Hash previousLedgerHash; + uint32 previousLedgerVersion; + LedgerCloseValueSignature lcValueSignature; + } proposedMsValue; } ext; }; diff --git a/xdr/Stellar-types.x b/xdr/Stellar-types.x index 670be6fd9f..9385455d4c 100644 --- a/xdr/Stellar-types.x +++ b/xdr/Stellar-types.x @@ -16,6 +16,8 @@ typedef hyper int64; typedef uint64 TimePoint; typedef uint64 Duration; +// Milliseconds since the Unix epoch. TimePoint is whole seconds. +typedef uint64 TimePointMs; // An ExtensionPoint is always marshaled as a 32-bit 0 value. At a // later point, it can be replaced by a different union so as to diff --git a/xdr/xdr_commit_generated.txt b/xdr/xdr_commit_generated.txt index f84526d1fb..3cb5e06af2 100644 --- a/xdr/xdr_commit_generated.txt +++ b/xdr/xdr_commit_generated.txt @@ -1 +1 @@ -ee040cd65310cd4f66d41fc74ebbbb3b6604dcc6 \ No newline at end of file +4f524bbac80c781c06e4fb5fc93a2d0d331d6705 \ No newline at end of file diff --git a/xdr/xdr_generated.go b/xdr/xdr_generated.go index 05ca24e934..f2d232b2df 100644 --- a/xdr/xdr_generated.go +++ b/xdr/xdr_generated.go @@ -33,18 +33,18 @@ import ( // XdrFilesSHA256 is the SHA256 hashes of source files. var XdrFilesSHA256 = map[string]string{ "xdr/Stellar-SCP.x": "6aed428fb6c2d000f5bc1eef0ba685d6108f3faa96208ffa588c0e2990813939", - "xdr/Stellar-contract-config-setting.x": "a034a3eb4d8b94f5c4c573fe14a1afc548aa316e1e897aa70e5a1688aada3c77", + "xdr/Stellar-contract-config-setting.x": "78327f22b47d931c92542d3b5543162dcf99830f409bd6cecc80410424742fb0", "xdr/Stellar-contract-env-meta.x": "75a271414d852096fea3283c63b7f2a702f2905f78fc28eb60ec7d7bd366a780", "xdr/Stellar-contract-meta.x": "f01532c11ca044e19d9f9f16fe373e9af64835da473be556b9a807ee3319ae0d", "xdr/Stellar-contract-spec.x": "943e65a0a8f5e8a5b19738ca7d029ce7a38fea4b1f9f59cb7d4262094882e03d", - "xdr/Stellar-contract.x": "1efa2e593e8a0e2ebbd3fde00e73da1d1afd10891a83e101dc46318673a34bbd", + "xdr/Stellar-contract.x": "e92d679eb8682b59ad0262422ddca3a43ad5498702ae2e90210bc1f5cead7a8c", "xdr/Stellar-exporter.x": "a00c83d02e8c8382e06f79a191f1fb5abd097a4bbcab8481c67467e3270e0529", "xdr/Stellar-internal.x": "227835866c1b2122d1eaf28839ba85ea7289d1cb681dda4ca619c2da3d71fe00", "xdr/Stellar-ledger-entries.x": "65a24350a69f0d1c74c0dce61a68db2a657611ad9318cb2736860fd99a2db020", - "xdr/Stellar-ledger.x": "6bce772b73dba183dae8f87da2a5d97fb0d36ad3695aec5edd21b1d4e5c0dc55", + "xdr/Stellar-ledger.x": "b98cc304cb7c7966d6d17842bbe9d7eb7e2af3039d6dafe306e52fa99900d6dc", "xdr/Stellar-overlay.x": "8c9b9c13c86fa4672f03d741705b41e7221be0fc48e1ea6eeb1ba07d31ec0723", "xdr/Stellar-transaction.x": "8f7accb9d9e0c077e6c5b43fc32eb06badb1cd3e6e10733387836dd8804ce89f", - "xdr/Stellar-types.x": "3ba2eb53dad5c7f4f10441d1af7a95778bf31bbbbe2a802ddc3b981910d7c397", + "xdr/Stellar-types.x": "3c56d9553b4b273967c4330db1cbdac8fba333c46f44d650fcdf2fe10744c46a", } var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") @@ -12201,20 +12201,26 @@ var _ xdrType = (*UpgradeType)(nil) // { // STELLAR_VALUE_BASIC = 0, // STELLAR_VALUE_SIGNED = 1, -// STELLAR_VALUE_EMPTY_TX_SET = 2 +// STELLAR_VALUE_EMPTY_TX_SET = 2, +// STELLAR_VALUE_SIGNED_MS = 3, +// STELLAR_VALUE_EMPTY_TX_SET_MS = 4 // }; type StellarValueType int32 const ( - StellarValueTypeStellarValueBasic StellarValueType = 0 - StellarValueTypeStellarValueSigned StellarValueType = 1 - StellarValueTypeStellarValueEmptyTxSet StellarValueType = 2 + StellarValueTypeStellarValueBasic StellarValueType = 0 + StellarValueTypeStellarValueSigned StellarValueType = 1 + StellarValueTypeStellarValueEmptyTxSet StellarValueType = 2 + StellarValueTypeStellarValueSignedMs StellarValueType = 3 + StellarValueTypeStellarValueEmptyTxSetMs StellarValueType = 4 ) var stellarValueTypeMap = map[int32]string{ 0: "StellarValueTypeStellarValueBasic", 1: "StellarValueTypeStellarValueSigned", 2: "StellarValueTypeStellarValueEmptyTxSet", + 3: "StellarValueTypeStellarValueSignedMs", + 4: "StellarValueTypeStellarValueEmptyTxSetMs", } // ValidEnum validates a proposed value for this enum. Implements @@ -12456,6 +12462,186 @@ func (s StellarValueProposedValue) xdrType() {} var _ xdrType = (*StellarValueProposedValue)(nil) +// StellarValueSignedMsValue is an XDR NestedStruct defines as: +// +// struct +// { +// TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 +// LedgerCloseValueSignature lcValueSignature; +// } +type StellarValueSignedMsValue struct { + CloseTimeMs TimePointMs + LcValueSignature LedgerCloseValueSignature +} + +// EncodeTo encodes this value using the Encoder. +func (s *StellarValueSignedMsValue) EncodeTo(e *xdr.Encoder) error { + var err error + if err = s.CloseTimeMs.EncodeTo(e); err != nil { + return err + } + if err = s.LcValueSignature.EncodeTo(e); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*StellarValueSignedMsValue)(nil) + +// DecodeFrom decodes this value using the Decoder. +func (s *StellarValueSignedMsValue) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding StellarValueSignedMsValue: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = s.CloseTimeMs.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding TimePointMs: %w", err) + } + nTmp, err = s.LcValueSignature.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding LedgerCloseValueSignature: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s StellarValueSignedMsValue) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *StellarValueSignedMsValue) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*StellarValueSignedMsValue)(nil) + _ encoding.BinaryUnmarshaler = (*StellarValueSignedMsValue)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s StellarValueSignedMsValue) xdrType() {} + +var _ xdrType = (*StellarValueSignedMsValue)(nil) + +// StellarValueProposedMsValue is an XDR NestedStruct defines as: +// +// struct +// { +// TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 +// Hash txSetHash; +// Hash previousLedgerHash; +// uint32 previousLedgerVersion; +// LedgerCloseValueSignature lcValueSignature; +// } +type StellarValueProposedMsValue struct { + CloseTimeMs TimePointMs + TxSetHash Hash + PreviousLedgerHash Hash + PreviousLedgerVersion Uint32 + LcValueSignature LedgerCloseValueSignature +} + +// EncodeTo encodes this value using the Encoder. +func (s *StellarValueProposedMsValue) EncodeTo(e *xdr.Encoder) error { + var err error + if err = s.CloseTimeMs.EncodeTo(e); err != nil { + return err + } + if err = s.TxSetHash.EncodeTo(e); err != nil { + return err + } + if err = s.PreviousLedgerHash.EncodeTo(e); err != nil { + return err + } + if err = s.PreviousLedgerVersion.EncodeTo(e); err != nil { + return err + } + if err = s.LcValueSignature.EncodeTo(e); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*StellarValueProposedMsValue)(nil) + +// DecodeFrom decodes this value using the Decoder. +func (s *StellarValueProposedMsValue) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding StellarValueProposedMsValue: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = s.CloseTimeMs.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding TimePointMs: %w", err) + } + nTmp, err = s.TxSetHash.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Hash: %w", err) + } + nTmp, err = s.PreviousLedgerHash.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Hash: %w", err) + } + nTmp, err = s.PreviousLedgerVersion.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Uint32: %w", err) + } + nTmp, err = s.LcValueSignature.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding LedgerCloseValueSignature: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s StellarValueProposedMsValue) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *StellarValueProposedMsValue) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*StellarValueProposedMsValue)(nil) + _ encoding.BinaryUnmarshaler = (*StellarValueProposedMsValue)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s StellarValueProposedMsValue) xdrType() {} + +var _ xdrType = (*StellarValueProposedMsValue)(nil) + // StellarValueExt is an XDR NestedUnion defines as: // // union switch (StellarValueType v) @@ -12472,11 +12658,28 @@ var _ xdrType = (*StellarValueProposedValue)(nil) // uint32 previousLedgerVersion; // LedgerCloseValueSignature lcValueSignature; // } proposedValue; +// case STELLAR_VALUE_SIGNED_MS: +// struct +// { +// TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 +// LedgerCloseValueSignature lcValueSignature; +// } signedMsValue; +// case STELLAR_VALUE_EMPTY_TX_SET_MS: +// struct +// { +// TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 +// Hash txSetHash; +// Hash previousLedgerHash; +// uint32 previousLedgerVersion; +// LedgerCloseValueSignature lcValueSignature; +// } proposedMsValue; // } type StellarValueExt struct { V StellarValueType LcValueSignature *LedgerCloseValueSignature ProposedValue *StellarValueProposedValue + SignedMsValue *StellarValueSignedMsValue + ProposedMsValue *StellarValueProposedMsValue } // SwitchFieldName returns the field name in which this union's @@ -12495,6 +12698,10 @@ func (u StellarValueExt) ArmForSwitch(sw int32) (string, bool) { return "LcValueSignature", true case StellarValueTypeStellarValueEmptyTxSet: return "ProposedValue", true + case StellarValueTypeStellarValueSignedMs: + return "SignedMsValue", true + case StellarValueTypeStellarValueEmptyTxSetMs: + return "ProposedMsValue", true } return "-", false } @@ -12519,6 +12726,20 @@ func NewStellarValueExt(v StellarValueType, value interface{}) (result StellarVa return } result.ProposedValue = &tv + case StellarValueTypeStellarValueSignedMs: + tv, ok := value.(StellarValueSignedMsValue) + if !ok { + err = errors.New("invalid value, must be StellarValueSignedMsValue") + return + } + result.SignedMsValue = &tv + case StellarValueTypeStellarValueEmptyTxSetMs: + tv, ok := value.(StellarValueProposedMsValue) + if !ok { + err = errors.New("invalid value, must be StellarValueProposedMsValue") + return + } + result.ProposedMsValue = &tv } return } @@ -12573,6 +12794,56 @@ func (u StellarValueExt) GetProposedValue() (result StellarValueProposedValue, o return } +// MustSignedMsValue retrieves the SignedMsValue value from the union, +// panicing if the value is not set. +func (u StellarValueExt) MustSignedMsValue() StellarValueSignedMsValue { + val, ok := u.GetSignedMsValue() + + if !ok { + panic("arm SignedMsValue is not set") + } + + return val +} + +// GetSignedMsValue retrieves the SignedMsValue value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u StellarValueExt) GetSignedMsValue() (result StellarValueSignedMsValue, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.V)) + + if armName == "SignedMsValue" { + result = *u.SignedMsValue + ok = true + } + + return +} + +// MustProposedMsValue retrieves the ProposedMsValue value from the union, +// panicing if the value is not set. +func (u StellarValueExt) MustProposedMsValue() StellarValueProposedMsValue { + val, ok := u.GetProposedMsValue() + + if !ok { + panic("arm ProposedMsValue is not set") + } + + return val +} + +// GetProposedMsValue retrieves the ProposedMsValue value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u StellarValueExt) GetProposedMsValue() (result StellarValueProposedMsValue, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.V)) + + if armName == "ProposedMsValue" { + result = *u.ProposedMsValue + ok = true + } + + return +} + // EncodeTo encodes this value using the Encoder. func (u StellarValueExt) EncodeTo(e *xdr.Encoder) error { var err error @@ -12593,6 +12864,16 @@ func (u StellarValueExt) EncodeTo(e *xdr.Encoder) error { return err } return nil + case StellarValueTypeStellarValueSignedMs: + if err = (*u.SignedMsValue).EncodeTo(e); err != nil { + return err + } + return nil + case StellarValueTypeStellarValueEmptyTxSetMs: + if err = (*u.ProposedMsValue).EncodeTo(e); err != nil { + return err + } + return nil } return fmt.Errorf("V (StellarValueType) switch value '%d' is not valid for union StellarValueExt", u.V) } @@ -12638,6 +12919,28 @@ func (u *StellarValueExt) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) return n, fmt.Errorf("decoding StellarValueProposedValue: %w", err) } return n, nil + case StellarValueTypeStellarValueSignedMs: + if err = xdr.TrackOutputBytesOf[StellarValueSignedMsValue](d); err != nil { + return n, fmt.Errorf("decoding StellarValueSignedMsValue: %w", err) + } + u.SignedMsValue = new(StellarValueSignedMsValue) + nTmp, err = (*u.SignedMsValue).DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding StellarValueSignedMsValue: %w", err) + } + return n, nil + case StellarValueTypeStellarValueEmptyTxSetMs: + if err = xdr.TrackOutputBytesOf[StellarValueProposedMsValue](d); err != nil { + return n, fmt.Errorf("decoding StellarValueProposedMsValue: %w", err) + } + u.ProposedMsValue = new(StellarValueProposedMsValue) + nTmp, err = (*u.ProposedMsValue).DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding StellarValueProposedMsValue: %w", err) + } + return n, nil } return n, fmt.Errorf("union StellarValueExt has invalid V (StellarValueType) switch value '%d'", u.V) } @@ -12699,6 +13002,21 @@ var _ xdrType = (*StellarValueExt)(nil) // uint32 previousLedgerVersion; // LedgerCloseValueSignature lcValueSignature; // } proposedValue; +// case STELLAR_VALUE_SIGNED_MS: +// struct +// { +// TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 +// LedgerCloseValueSignature lcValueSignature; +// } signedMsValue; +// case STELLAR_VALUE_EMPTY_TX_SET_MS: +// struct +// { +// TimePointMs closeTimeMs; // closeTime == closeTimeMs / 1000 +// Hash txSetHash; +// Hash previousLedgerHash; +// uint32 previousLedgerVersion; +// LedgerCloseValueSignature lcValueSignature; +// } proposedMsValue; // } // ext; // }; @@ -51126,6 +51444,66 @@ func (s Duration) xdrType() {} var _ xdrType = (*Duration)(nil) +// TimePointMs is an XDR Typedef defines as: +// +// typedef uint64 TimePointMs; +type TimePointMs Uint64 + +// EncodeTo encodes this value using the Encoder. +func (s TimePointMs) EncodeTo(e *xdr.Encoder) error { + var err error + if err = Uint64(s).EncodeTo(e); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*TimePointMs)(nil) + +// DecodeFrom decodes this value using the Decoder. +func (s *TimePointMs) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding TimePointMs: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = (*Uint64)(s).DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Uint64: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s TimePointMs) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *TimePointMs) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*TimePointMs)(nil) + _ encoding.BinaryUnmarshaler = (*TimePointMs)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s TimePointMs) xdrType() {} + +var _ xdrType = (*TimePointMs)(nil) + // ExtensionPoint is an XDR Union defines as: // // union ExtensionPoint switch (int v) @@ -59104,8 +59482,7 @@ var _ xdrType = (*ContractExecutableType)(nil) // SC_ADDRESS_TYPE_CONTRACT = 1, // SC_ADDRESS_TYPE_MUXED_ACCOUNT = 2, // SC_ADDRESS_TYPE_CLAIMABLE_BALANCE = 3, -// SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4 -// , +// SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4, // SC_ADDRESS_TYPE_MUXED_CONTRACT = 5 // }; type ScAddressType int32 @@ -63530,7 +63907,25 @@ var _ xdrType = (*ConfigSettingContractBandwidthV0)(nil) // // Cost of performing BN254 scalar element inversion // Bn254FrInv = 84, // // Cost of performing BN254 G1 multi-scalar multiplication (MSM) -// Bn254G1Msm = 85 +// Bn254G1Msm = 85, +// // Cost of decoding and expanding an ML-DSA-44 verifying key +// MlDsa44DecodeVerifyingKey = 86, +// // Cost of decoding and expanding an ML-DSA-65 verifying key +// MlDsa65DecodeVerifyingKey = 87, +// // Cost of decoding and expanding an ML-DSA-87 verifying key +// MlDsa87DecodeVerifyingKey = 88, +// // Cost of decoding an ML-DSA-44 signature +// MlDsa44DecodeSignature = 89, +// // Cost of decoding an ML-DSA-65 signature +// MlDsa65DecodeSignature = 90, +// // Cost of decoding an ML-DSA-87 signature +// MlDsa87DecodeSignature = 91, +// // Cost of verifying an ML-DSA-44 signature, linear in message + context length +// VerifyMlDsa44Sig = 92, +// // Cost of verifying an ML-DSA-65 signature, linear in message + context length +// VerifyMlDsa65Sig = 93, +// // Cost of verifying an ML-DSA-87 signature, linear in message + context length +// VerifyMlDsa87Sig = 94 // }; type ContractCostType int32 @@ -63621,6 +64016,15 @@ const ( ContractCostTypeBn254FrPow ContractCostType = 83 ContractCostTypeBn254FrInv ContractCostType = 84 ContractCostTypeBn254G1Msm ContractCostType = 85 + ContractCostTypeMlDsa44DecodeVerifyingKey ContractCostType = 86 + ContractCostTypeMlDsa65DecodeVerifyingKey ContractCostType = 87 + ContractCostTypeMlDsa87DecodeVerifyingKey ContractCostType = 88 + ContractCostTypeMlDsa44DecodeSignature ContractCostType = 89 + ContractCostTypeMlDsa65DecodeSignature ContractCostType = 90 + ContractCostTypeMlDsa87DecodeSignature ContractCostType = 91 + ContractCostTypeVerifyMlDsa44Sig ContractCostType = 92 + ContractCostTypeVerifyMlDsa65Sig ContractCostType = 93 + ContractCostTypeVerifyMlDsa87Sig ContractCostType = 94 ) var contractCostTypeMap = map[int32]string{ @@ -63710,6 +64114,15 @@ var contractCostTypeMap = map[int32]string{ 83: "ContractCostTypeBn254FrPow", 84: "ContractCostTypeBn254FrInv", 85: "ContractCostTypeBn254G1Msm", + 86: "ContractCostTypeMlDsa44DecodeVerifyingKey", + 87: "ContractCostTypeMlDsa65DecodeVerifyingKey", + 88: "ContractCostTypeMlDsa87DecodeVerifyingKey", + 89: "ContractCostTypeMlDsa44DecodeSignature", + 90: "ContractCostTypeMlDsa65DecodeSignature", + 91: "ContractCostTypeMlDsa87DecodeSignature", + 92: "ContractCostTypeVerifyMlDsa44Sig", + 93: "ContractCostTypeVerifyMlDsa65Sig", + 94: "ContractCostTypeVerifyMlDsa87Sig", } // ValidEnum validates a proposed value for this enum. Implements diff --git a/xdr/xdr_views_generated.go b/xdr/xdr_views_generated.go index d658ad3bf8..3559ea4e09 100644 --- a/xdr/xdr_views_generated.go +++ b/xdr/xdr_views_generated.go @@ -4099,7 +4099,7 @@ func (v ContractCostTypeView) Value() (ContractCostType, error) { } val := ContractCostType(int32(binary.BigEndian.Uint32(v[:4]))) switch val { - case ContractCostTypeWasmInsnExec, ContractCostTypeMemAlloc, ContractCostTypeMemCpy, ContractCostTypeMemCmp, ContractCostTypeDispatchHostFunction, ContractCostTypeVisitObject, ContractCostTypeValSer, ContractCostTypeValDeser, ContractCostTypeComputeSha256Hash, ContractCostTypeComputeEd25519PubKey, ContractCostTypeVerifyEd25519Sig, ContractCostTypeVmInstantiation, ContractCostTypeVmCachedInstantiation, ContractCostTypeInvokeVmFunction, ContractCostTypeComputeKeccak256Hash, ContractCostTypeDecodeEcdsaCurve256Sig, ContractCostTypeRecoverEcdsaSecp256k1Key, ContractCostTypeInt256AddSub, ContractCostTypeInt256Mul, ContractCostTypeInt256Div, ContractCostTypeInt256Pow, ContractCostTypeInt256Shift, ContractCostTypeChaCha20DrawBytes, ContractCostTypeParseWasmInstructions, ContractCostTypeParseWasmFunctions, ContractCostTypeParseWasmGlobals, ContractCostTypeParseWasmTableEntries, ContractCostTypeParseWasmTypes, ContractCostTypeParseWasmDataSegments, ContractCostTypeParseWasmElemSegments, ContractCostTypeParseWasmImports, ContractCostTypeParseWasmExports, ContractCostTypeParseWasmDataSegmentBytes, ContractCostTypeInstantiateWasmInstructions, ContractCostTypeInstantiateWasmFunctions, ContractCostTypeInstantiateWasmGlobals, ContractCostTypeInstantiateWasmTableEntries, ContractCostTypeInstantiateWasmTypes, ContractCostTypeInstantiateWasmDataSegments, ContractCostTypeInstantiateWasmElemSegments, ContractCostTypeInstantiateWasmImports, ContractCostTypeInstantiateWasmExports, ContractCostTypeInstantiateWasmDataSegmentBytes, ContractCostTypeSec1DecodePointUncompressed, ContractCostTypeVerifyEcdsaSecp256r1Sig, ContractCostTypeBls12381EncodeFp, ContractCostTypeBls12381DecodeFp, ContractCostTypeBls12381G1CheckPointOnCurve, ContractCostTypeBls12381G1CheckPointInSubgroup, ContractCostTypeBls12381G2CheckPointOnCurve, ContractCostTypeBls12381G2CheckPointInSubgroup, ContractCostTypeBls12381G1ProjectiveToAffine, ContractCostTypeBls12381G2ProjectiveToAffine, ContractCostTypeBls12381G1Add, ContractCostTypeBls12381G1Mul, ContractCostTypeBls12381G1Msm, ContractCostTypeBls12381MapFpToG1, ContractCostTypeBls12381HashToG1, ContractCostTypeBls12381G2Add, ContractCostTypeBls12381G2Mul, ContractCostTypeBls12381G2Msm, ContractCostTypeBls12381MapFp2ToG2, ContractCostTypeBls12381HashToG2, ContractCostTypeBls12381Pairing, ContractCostTypeBls12381FrFromU256, ContractCostTypeBls12381FrToU256, ContractCostTypeBls12381FrAddSub, ContractCostTypeBls12381FrMul, ContractCostTypeBls12381FrPow, ContractCostTypeBls12381FrInv, ContractCostTypeBn254EncodeFp, ContractCostTypeBn254DecodeFp, ContractCostTypeBn254G1CheckPointOnCurve, ContractCostTypeBn254G2CheckPointOnCurve, ContractCostTypeBn254G2CheckPointInSubgroup, ContractCostTypeBn254G1ProjectiveToAffine, ContractCostTypeBn254G1Add, ContractCostTypeBn254G1Mul, ContractCostTypeBn254Pairing, ContractCostTypeBn254FrFromU256, ContractCostTypeBn254FrToU256, ContractCostTypeBn254FrAddSub, ContractCostTypeBn254FrMul, ContractCostTypeBn254FrPow, ContractCostTypeBn254FrInv, ContractCostTypeBn254G1Msm: + case ContractCostTypeWasmInsnExec, ContractCostTypeMemAlloc, ContractCostTypeMemCpy, ContractCostTypeMemCmp, ContractCostTypeDispatchHostFunction, ContractCostTypeVisitObject, ContractCostTypeValSer, ContractCostTypeValDeser, ContractCostTypeComputeSha256Hash, ContractCostTypeComputeEd25519PubKey, ContractCostTypeVerifyEd25519Sig, ContractCostTypeVmInstantiation, ContractCostTypeVmCachedInstantiation, ContractCostTypeInvokeVmFunction, ContractCostTypeComputeKeccak256Hash, ContractCostTypeDecodeEcdsaCurve256Sig, ContractCostTypeRecoverEcdsaSecp256k1Key, ContractCostTypeInt256AddSub, ContractCostTypeInt256Mul, ContractCostTypeInt256Div, ContractCostTypeInt256Pow, ContractCostTypeInt256Shift, ContractCostTypeChaCha20DrawBytes, ContractCostTypeParseWasmInstructions, ContractCostTypeParseWasmFunctions, ContractCostTypeParseWasmGlobals, ContractCostTypeParseWasmTableEntries, ContractCostTypeParseWasmTypes, ContractCostTypeParseWasmDataSegments, ContractCostTypeParseWasmElemSegments, ContractCostTypeParseWasmImports, ContractCostTypeParseWasmExports, ContractCostTypeParseWasmDataSegmentBytes, ContractCostTypeInstantiateWasmInstructions, ContractCostTypeInstantiateWasmFunctions, ContractCostTypeInstantiateWasmGlobals, ContractCostTypeInstantiateWasmTableEntries, ContractCostTypeInstantiateWasmTypes, ContractCostTypeInstantiateWasmDataSegments, ContractCostTypeInstantiateWasmElemSegments, ContractCostTypeInstantiateWasmImports, ContractCostTypeInstantiateWasmExports, ContractCostTypeInstantiateWasmDataSegmentBytes, ContractCostTypeSec1DecodePointUncompressed, ContractCostTypeVerifyEcdsaSecp256r1Sig, ContractCostTypeBls12381EncodeFp, ContractCostTypeBls12381DecodeFp, ContractCostTypeBls12381G1CheckPointOnCurve, ContractCostTypeBls12381G1CheckPointInSubgroup, ContractCostTypeBls12381G2CheckPointOnCurve, ContractCostTypeBls12381G2CheckPointInSubgroup, ContractCostTypeBls12381G1ProjectiveToAffine, ContractCostTypeBls12381G2ProjectiveToAffine, ContractCostTypeBls12381G1Add, ContractCostTypeBls12381G1Mul, ContractCostTypeBls12381G1Msm, ContractCostTypeBls12381MapFpToG1, ContractCostTypeBls12381HashToG1, ContractCostTypeBls12381G2Add, ContractCostTypeBls12381G2Mul, ContractCostTypeBls12381G2Msm, ContractCostTypeBls12381MapFp2ToG2, ContractCostTypeBls12381HashToG2, ContractCostTypeBls12381Pairing, ContractCostTypeBls12381FrFromU256, ContractCostTypeBls12381FrToU256, ContractCostTypeBls12381FrAddSub, ContractCostTypeBls12381FrMul, ContractCostTypeBls12381FrPow, ContractCostTypeBls12381FrInv, ContractCostTypeBn254EncodeFp, ContractCostTypeBn254DecodeFp, ContractCostTypeBn254G1CheckPointOnCurve, ContractCostTypeBn254G2CheckPointOnCurve, ContractCostTypeBn254G2CheckPointInSubgroup, ContractCostTypeBn254G1ProjectiveToAffine, ContractCostTypeBn254G1Add, ContractCostTypeBn254G1Mul, ContractCostTypeBn254Pairing, ContractCostTypeBn254FrFromU256, ContractCostTypeBn254FrToU256, ContractCostTypeBn254FrAddSub, ContractCostTypeBn254FrMul, ContractCostTypeBn254FrPow, ContractCostTypeBn254FrInv, ContractCostTypeBn254G1Msm, ContractCostTypeMlDsa44DecodeVerifyingKey, ContractCostTypeMlDsa65DecodeVerifyingKey, ContractCostTypeMlDsa87DecodeVerifyingKey, ContractCostTypeMlDsa44DecodeSignature, ContractCostTypeMlDsa65DecodeSignature, ContractCostTypeMlDsa87DecodeSignature, ContractCostTypeVerifyMlDsa44Sig, ContractCostTypeVerifyMlDsa65Sig, ContractCostTypeVerifyMlDsa87Sig: return val, nil default: return 0, viewErrUnknownDiscriminant(0, int32(val)) @@ -30356,7 +30356,7 @@ func (v StellarValueTypeView) Value() (StellarValueType, error) { } val := StellarValueType(int32(binary.BigEndian.Uint32(v[:4]))) switch val { - case StellarValueTypeStellarValueBasic, StellarValueTypeStellarValueSigned, StellarValueTypeStellarValueEmptyTxSet: + case StellarValueTypeStellarValueBasic, StellarValueTypeStellarValueSigned, StellarValueTypeStellarValueEmptyTxSet, StellarValueTypeStellarValueSignedMs, StellarValueTypeStellarValueEmptyTxSetMs: return val, nil default: return 0, viewErrUnknownDiscriminant(0, int32(val)) @@ -30691,6 +30691,350 @@ func (v StellarValueProposedValueView) Fields() (StellarValueProposedValueFields return locateStellarValueProposedValue(v) } +type StellarValueSignedMsValueView []byte + +func (v StellarValueSignedMsValueView) size(depth int) (int, error) { + if depth > maxDepth { + return 0, viewErrMaxDepth(0) + } + off := int64(0) + off += 8 + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + { + sz, err := LedgerCloseValueSignatureView(v[off:]).size(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return int(off), nil +} +func (v StellarValueSignedMsValueView) CloseTimeMs() (TimePointMsView, error) { + return TimePointMsView(v[0:]), nil +} +func (v StellarValueSignedMsValueView) MustCloseTimeMs() TimePointMsView { + return must(v.CloseTimeMs()) +} +func (v StellarValueSignedMsValueView) LcValueSignature() (LedgerCloseValueSignatureView, error) { + off := int64(0) + off += 8 + if off > int64(len(v)) { + return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return LedgerCloseValueSignatureView(v[off:]), nil +} +func (v StellarValueSignedMsValueView) MustLcValueSignature() LedgerCloseValueSignatureView { + return must(v.LcValueSignature()) +} +func (v StellarValueSignedMsValueView) valid(depth int) (int, error) { + if depth > maxDepth { + return 0, viewErrMaxDepth(0) + } + off := int64(0) + { + sz, err := TimePointMsView(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + { + sz, err := LedgerCloseValueSignatureView(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + return int(off), nil +} + +// Raw returns the exact wire bytes for this view, trimmed from the fat slice. +func (v StellarValueSignedMsValueView) Raw() ([]byte, error) { return viewRaw(v) } + +// Copy returns an independent copy of this view that does not alias the original bytes. +func (v StellarValueSignedMsValueView) Copy() (StellarValueSignedMsValueView, error) { + return viewCopy(v) +} + +// ValidateFull checks that this view is well-formed: bounds, schema constraints, and depth limits. +func (v StellarValueSignedMsValueView) ValidateFull() error { return validate(v) } +func (v StellarValueSignedMsValueView) MustRaw() []byte { return must(v.Raw()) } +func (v StellarValueSignedMsValueView) MustCopy() StellarValueSignedMsValueView { + return must(v.Copy()) +} + +// StellarValueSignedMsValueFields is the located form of StellarValueSignedMsValueView: every field trimmed to its exact wire extent, all found in one walk. +type StellarValueSignedMsValueFields struct { + View StellarValueSignedMsValueView + CloseTimeMs TimePointMsView + LcValueSignature LedgerCloseValueSignatureView +} + +func locateStellarValueSignedMsValue(v StellarValueSignedMsValueView) (StellarValueSignedMsValueFields, error) { + var f StellarValueSignedMsValueFields + off := int64(0) + if off+8 > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.CloseTimeMs = TimePointMsView(v[off : off+8]) + off += 8 + if off > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + { + sz, err := LedgerCloseValueSignatureView(v[off:]).size(0) + if err != nil { + return f, err + } + fsz := int64(sz) + if off+fsz > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.LcValueSignature = LedgerCloseValueSignatureView(v[off : off+fsz]) + off += fsz + } + if off > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.View = StellarValueSignedMsValueView(v[:off]) + return f, nil +} + +// Fields locates every field of this node in a single walk, each trimmed to its exact wire extent. +func (v StellarValueSignedMsValueView) Fields() (StellarValueSignedMsValueFields, error) { + return locateStellarValueSignedMsValue(v) +} + +type StellarValueProposedMsValueView []byte + +func (v StellarValueProposedMsValueView) size(depth int) (int, error) { + if depth > maxDepth { + return 0, viewErrMaxDepth(0) + } + off := int64(0) + off += 8 + off += 32 + off += 32 + off += 4 + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + { + sz, err := LedgerCloseValueSignatureView(v[off:]).size(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return int(off), nil +} +func (v StellarValueProposedMsValueView) CloseTimeMs() (TimePointMsView, error) { + return TimePointMsView(v[0:]), nil +} +func (v StellarValueProposedMsValueView) MustCloseTimeMs() TimePointMsView { + return must(v.CloseTimeMs()) +} +func (v StellarValueProposedMsValueView) TxSetHash() (HashView, error) { + off := int64(0) + off += 8 + if off > int64(len(v)) { + return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return HashView(v[off:]), nil +} +func (v StellarValueProposedMsValueView) MustTxSetHash() HashView { return must(v.TxSetHash()) } +func (v StellarValueProposedMsValueView) PreviousLedgerHash() (HashView, error) { + off := int64(0) + off += 8 + off += 32 + if off > int64(len(v)) { + return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return HashView(v[off:]), nil +} +func (v StellarValueProposedMsValueView) MustPreviousLedgerHash() HashView { + return must(v.PreviousLedgerHash()) +} +func (v StellarValueProposedMsValueView) PreviousLedgerVersion() (Uint32View, error) { + off := int64(0) + off += 8 + off += 32 + off += 32 + if off > int64(len(v)) { + return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return Uint32View(v[off:]), nil +} +func (v StellarValueProposedMsValueView) MustPreviousLedgerVersion() Uint32View { + return must(v.PreviousLedgerVersion()) +} +func (v StellarValueProposedMsValueView) LcValueSignature() (LedgerCloseValueSignatureView, error) { + off := int64(0) + off += 8 + off += 32 + off += 32 + off += 4 + if off > int64(len(v)) { + return nil, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + return LedgerCloseValueSignatureView(v[off:]), nil +} +func (v StellarValueProposedMsValueView) MustLcValueSignature() LedgerCloseValueSignatureView { + return must(v.LcValueSignature()) +} +func (v StellarValueProposedMsValueView) valid(depth int) (int, error) { + if depth > maxDepth { + return 0, viewErrMaxDepth(0) + } + off := int64(0) + { + sz, err := TimePointMsView(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + { + sz, err := HashView(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + { + sz, err := HashView(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + { + sz, err := Uint32View(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + { + sz, err := LedgerCloseValueSignatureView(v[off:]).valid(depth + 1) + if err != nil { + return 0, err + } + off += int64(sz) + if off > int64(len(v)) { + return 0, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + } + return int(off), nil +} + +// Raw returns the exact wire bytes for this view, trimmed from the fat slice. +func (v StellarValueProposedMsValueView) Raw() ([]byte, error) { return viewRaw(v) } + +// Copy returns an independent copy of this view that does not alias the original bytes. +func (v StellarValueProposedMsValueView) Copy() (StellarValueProposedMsValueView, error) { + return viewCopy(v) +} + +// ValidateFull checks that this view is well-formed: bounds, schema constraints, and depth limits. +func (v StellarValueProposedMsValueView) ValidateFull() error { return validate(v) } +func (v StellarValueProposedMsValueView) MustRaw() []byte { return must(v.Raw()) } +func (v StellarValueProposedMsValueView) MustCopy() StellarValueProposedMsValueView { + return must(v.Copy()) +} + +// StellarValueProposedMsValueFields is the located form of StellarValueProposedMsValueView: every field trimmed to its exact wire extent, all found in one walk. +type StellarValueProposedMsValueFields struct { + View StellarValueProposedMsValueView + CloseTimeMs TimePointMsView + TxSetHash HashView + PreviousLedgerHash HashView + PreviousLedgerVersion Uint32View + LcValueSignature LedgerCloseValueSignatureView +} + +func locateStellarValueProposedMsValue(v StellarValueProposedMsValueView) (StellarValueProposedMsValueFields, error) { + var f StellarValueProposedMsValueFields + off := int64(0) + if off+8 > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.CloseTimeMs = TimePointMsView(v[off : off+8]) + off += 8 + if off+32 > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.TxSetHash = HashView(v[off : off+32]) + off += 32 + if off+32 > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.PreviousLedgerHash = HashView(v[off : off+32]) + off += 32 + if off+4 > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.PreviousLedgerVersion = Uint32View(v[off : off+4]) + off += 4 + if off > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + { + sz, err := LedgerCloseValueSignatureView(v[off:]).size(0) + if err != nil { + return f, err + } + fsz := int64(sz) + if off+fsz > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.LcValueSignature = LedgerCloseValueSignatureView(v[off : off+fsz]) + off += fsz + } + if off > int64(len(v)) { + return f, viewErrShortBuffer(uint32(off), "field offset exceeds data") + } + f.View = StellarValueProposedMsValueView(v[:off]) + return f, nil +} + +// Fields locates every field of this node in a single walk, each trimmed to its exact wire extent. +func (v StellarValueProposedMsValueView) Fields() (StellarValueProposedMsValueFields, error) { + return locateStellarValueProposedMsValue(v) +} + type StellarValueExtView []byte func (v StellarValueExtView) size(depth int) (int, error) { @@ -30722,6 +31066,24 @@ func (v StellarValueExtView) size(depth int) (int, error) { return 0, viewErrShortBuffer(4, "arm exceeds data") } return 4 + sz, nil + case int32(StellarValueTypeStellarValueSignedMs): + sz, err := StellarValueSignedMsValueView(v[4:]).size(depth + 1) + if err != nil { + return 0, err + } + if 4+sz > len(v) { + return 0, viewErrShortBuffer(4, "arm exceeds data") + } + return 4 + sz, nil + case int32(StellarValueTypeStellarValueEmptyTxSetMs): + sz, err := StellarValueProposedMsValueView(v[4:]).size(depth + 1) + if err != nil { + return 0, err + } + if 4+sz > len(v) { + return 0, viewErrShortBuffer(4, "arm exceeds data") + } + return 4 + sz, nil default: return 0, viewErrUnknownDiscriminant(0, disc) } @@ -30732,7 +31094,7 @@ func (v StellarValueExtView) V() (StellarValueType, error) { } val := StellarValueType(int32(binary.BigEndian.Uint32(v[:4]))) switch val { - case StellarValueTypeStellarValueBasic, StellarValueTypeStellarValueSigned, StellarValueTypeStellarValueEmptyTxSet: + case StellarValueTypeStellarValueBasic, StellarValueTypeStellarValueSigned, StellarValueTypeStellarValueEmptyTxSet, StellarValueTypeStellarValueSignedMs, StellarValueTypeStellarValueEmptyTxSetMs: return val, nil default: return 0, viewErrUnknownDiscriminant(0, int32(val)) @@ -30769,6 +31131,36 @@ func (v StellarValueExtView) ProposedValue() (StellarValueProposedValueView, err func (v StellarValueExtView) MustProposedValue() StellarValueProposedValueView { return must(v.ProposedValue()) } +func (v StellarValueExtView) SignedMsValue() (StellarValueSignedMsValueView, error) { + if len(v) < 4 { + return nil, viewErrShortBuffer(0, "need 4 bytes for discriminant") + } + disc := int32(binary.BigEndian.Uint32(v[:4])) + switch disc { + case int32(StellarValueTypeStellarValueSignedMs): + default: + return nil, viewErrWrongDiscriminant(0, disc, int32(StellarValueTypeStellarValueSignedMs)) + } + return StellarValueSignedMsValueView(v[4:]), nil +} +func (v StellarValueExtView) MustSignedMsValue() StellarValueSignedMsValueView { + return must(v.SignedMsValue()) +} +func (v StellarValueExtView) ProposedMsValue() (StellarValueProposedMsValueView, error) { + if len(v) < 4 { + return nil, viewErrShortBuffer(0, "need 4 bytes for discriminant") + } + disc := int32(binary.BigEndian.Uint32(v[:4])) + switch disc { + case int32(StellarValueTypeStellarValueEmptyTxSetMs): + default: + return nil, viewErrWrongDiscriminant(0, disc, int32(StellarValueTypeStellarValueEmptyTxSetMs)) + } + return StellarValueProposedMsValueView(v[4:]), nil +} +func (v StellarValueExtView) MustProposedMsValue() StellarValueProposedMsValueView { + return must(v.ProposedMsValue()) +} func (v StellarValueExtView) valid(depth int) (int, error) { if depth > maxDepth { return 0, viewErrMaxDepth(0) @@ -30798,6 +31190,24 @@ func (v StellarValueExtView) valid(depth int) (int, error) { return 0, viewErrShortBuffer(4, "arm exceeds data") } return 4 + sz, nil + case int32(StellarValueTypeStellarValueSignedMs): + sz, err := StellarValueSignedMsValueView(v[4:]).valid(depth + 1) + if err != nil { + return 0, err + } + if 4+sz > len(v) { + return 0, viewErrShortBuffer(4, "arm exceeds data") + } + return 4 + sz, nil + case int32(StellarValueTypeStellarValueEmptyTxSetMs): + sz, err := StellarValueProposedMsValueView(v[4:]).valid(depth + 1) + if err != nil { + return 0, err + } + if 4+sz > len(v) { + return 0, viewErrShortBuffer(4, "arm exceeds data") + } + return 4 + sz, nil default: return 0, viewErrUnknownDiscriminant(0, disc) } @@ -77983,6 +78393,7 @@ func (v Uint256View) MustCopy() Uint256View { return must(v.Copy()) } type TimePointView = Uint64View type DurationView = Uint64View +type TimePointMsView = Uint64View type ExtensionPointView []byte func (v ExtensionPointView) size(_ int) (int, error) { return 4, nil } From b9daf77024b35e3d14d20e3420ae2a9cc48033c5 Mon Sep 17 00:00:00 2001 From: tamirms Date: Thu, 8 Oct 2026 10:43:42 +0100 Subject: [PATCH 07/10] strkey: test a real checksum mismatch for muxed contract addresses The 'bad checksum' W vector differed from a valid address only in the unused padding bit, so it was rejected as non-canonical before the CRC was checked. Keep it as a non-canonical case and add a vector with a real CRC mismatch. Co-Authored-By: Claude Opus 5.5 --- strkey/muxed_contract_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/strkey/muxed_contract_test.go b/strkey/muxed_contract_test.go index 2b3505d161..07470fb39d 100644 --- a/strkey/muxed_contract_test.go +++ b/strkey/muxed_contract_test.go @@ -53,8 +53,10 @@ func TestDecodeMuxedContract(t *testing.T) { for _, invalid := range []string{ muxedContractBase, "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK", - // bad checksum + // non-canonical: differs from a valid address only in the unused padding bit "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IH", + // bad checksum + "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IE", // truncated "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6", "", @@ -66,6 +68,16 @@ func TestDecodeMuxedContract(t *testing.T) { } } +func TestDecodeMuxedContractRejectionReasons(t *testing.T) { + _, err := Decode(VersionByteMuxedContract, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IE") + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid checksum") + + _, err = Decode(VersionByteMuxedContract, "WA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCIA6IH") + require.Error(t, err) + assert.Contains(t, err.Error(), "non-canonical") +} + func TestMuxedContract_SetContractID(t *testing.T) { muxed := MuxedContract{} assert.EqualError(t, muxed.SetContractID(""), "invalid contract address") From dde8ed408555cc6ca6da037398004256b2937687 Mon Sep 17 00:00:00 2001 From: tamirms Date: Thu, 8 Oct 2026 10:43:42 +0100 Subject: [PATCH 08/10] support/contractevents: read the amount from map event data A SAC transfer to a muxed destination (CAP-0067 M, CAP-0084 W) carries {amount, to_muxed_id} as its data. parseBalanceChangeEvent only accepted a bare i128 and, because an earlier assignment had cleared err, returned a zero amount with a nil error. Read the amount from the map form and return ErrNotBalanceChangeEvent when there is no amount. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 5 ++++- support/contractevents/event_test.go | 29 ++++++++++++++++++++++++++++ support/contractevents/utils.go | 23 ++++++++++++++++++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a7467415..12323b909f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,16 +13,19 @@ Official project releases may be found here: https://github.com/stellar/go-stell ## Pending ### Breaking Changes -* xdr: Regenerated from stellar-xdr@ee040cd6 with `CAP_0084_MUXED_CONTRACT` enabled. Contract spec UDT and event names are widened to `SC_SPEC_TYPE_NAME_LIMIT` (1024 bytes), and `ScSpecEventV0.Name` is now a `string` instead of an `ScSymbol`. +* xdr: Protocol 30 support (CAP-0084, CAP-0087, CAP-0088). Regenerated from stellar-xdr@4f524bba, where all three CAPs are ungated, so `XDR_FEATURES` is now empty. Contract spec UDT and event names are widened to `SC_SPEC_TYPE_NAME_LIMIT` (1024 bytes), and `ScSpecEventV0.Name` is now a `string` instead of an `ScSymbol`. ### New Features * xdr: Add the CAP-0084 `ScAddressTypeScAddressTypeMuxedContract` arm of `ScAddress`. `ScAddress.String()` renders it as a `W...` muxed contract strkey. +* xdr: Add the CAP-0088 millisecond close-time arms of `StellarValue`: `StellarValueTypeStellarValueSignedMs` (`SignedMsValue`) and `StellarValueTypeStellarValueEmptyTxSetMs` (`ProposedMsValue`), and the `TimePointMs` type. Every ledger after the Protocol 30 upgrade ledger uses one of these arms, so earlier SDK versions cannot decode them. +* xdr: Add the CAP-0087 ML-DSA `ContractCostType` values 86–94 (`MlDsa{44,65,87}DecodeVerifyingKey`, `MlDsa{44,65,87}DecodeSignature`, `VerifyMlDsa{44,65,87}Sig`). These are names only; cost params are positional, so they decoded before. * strkey: Add `VersionByteMuxedContract` (`W...`), `MuxedContract`, `DecodeMuxedContract`, and `IsValidMuxedContractAddress`. * xdr: Added `LedgerCloseMetaView.LedgerHeader()`, exposing the version-resolving header accessor that already backs `LedgerSequence`, `LedgerCloseTime`, `LedgerHash`, and `PreviousLedgerHash` ([#5982](https://github.com/stellar/go-stellar-sdk/pull/5982)) * rpcclient: Add `Client.URL()` to expose the configured RPC server URL ([#5885](https://github.com/stellar/go-stellar-sdk/issues/5885)) * protocols/rpc: Add queryEvents wire types and request validation (`QueryEventsRequest`/`QueryEventsResponse`, `QueryEventsFilter`, scan statuses, typed `error.data` payloads), transcribed from the [accepted proposal](https://github.com/orgs/stellar/discussions/1872) ([#5971](https://github.com/stellar/go-stellar-sdk/pull/5971)) ### Bug Fixes +* support/contractevents: Read the amount from map event data (`{amount, to_muxed_id}`). A SAC transfer to a muxed destination (CAP-0067 `M...`, CAP-0084 `W...`) previously parsed as a transfer with `Amount` zero and no error. Event data with no amount now returns `ErrNotTransferEvent` instead of a zero-amount event. * processors/token_transfer: Accept a `to_muxed_id` bound to `Void` in V4 event data. CAP-0067 specifies that the key is simply absent when there is no muxed destination, and that form already parsed. `Void` is what a contract emits instead if it publishes its event data as a `#[contracttype]` struct with an `Option` field — the natural way to write it before CAP-0086's sparse maps, which omit the key. Such an event previously failed to parse and was dropped from the event stream entirely, silently ([#5983](https://github.com/stellar/go-stellar-sdk/pull/5983)) * processors/token_transfer: Report a `to_muxed_id` of type `ScvBytes` at the length the contract emitted. It was previously copied into a fixed 32-byte buffer, so a shorter value was right-padded with zeroes and a longer one truncated, reporting a muxed id that was never emitted. Only a classic transaction memo maps to a fixed 32 bytes here; a contract may put any byte string in `to_muxed_id` ([#5984](https://github.com/stellar/go-stellar-sdk/issues/5984)) diff --git a/support/contractevents/event_test.go b/support/contractevents/event_test.go index f9358ec3ab..3d5c039c34 100644 --- a/support/contractevents/event_test.go +++ b/support/contractevents/event_test.go @@ -103,6 +103,35 @@ func TestSACTransferEvent(t *testing.T) { require.EqualValues(t, 0, transferEvent.Amount.Hi) } +// TestSACTransferEventMapData covers the event data shape a SAC emits for a +// transfer to a muxed destination (CAP-0067 M addresses, CAP-0084 W addresses): +// the topics carry the plain address and the data is {amount, to_muxed_id}. +func TestSACTransferEventMapData(t *testing.T) { + xdrEvent := GenerateEvent(EventTypeTransfer, randomAccount, zeroContract, "", randomAsset, big.NewInt(1000), passphrase) + amount := xdrEvent.Body.V0.Data + mapData := xdr.ScMap{ + {Key: makeSymbol("amount"), Val: amount}, + {Key: makeSymbol("to_muxed_id"), Val: xdr.ScVal{Type: xdr.ScValTypeScvU64, U64: func() *xdr.Uint64 { v := xdr.Uint64(7); return &v }()}}, + } + mapPtr := &mapData + xdrEvent.Body.V0.Data = xdr.ScVal{Type: xdr.ScValTypeScvMap, Map: &mapPtr} + + sacEvent, err := NewStellarAssetContractEvent(&xdrEvent, passphrase) + require.NoError(t, err) + transferEvent := sacEvent.(*TransferEvent) + require.Equal(t, zeroContract, transferEvent.To) + require.EqualValues(t, 1000, transferEvent.Amount.Lo) + require.EqualValues(t, 0, transferEvent.Amount.Hi) + + // A map without an "amount" key is not a balance change; it must not + // parse as a zero-amount transfer. + noAmount := xdr.ScMap{mapData[1]} + noAmountPtr := &noAmount + xdrEvent.Body.V0.Data = xdr.ScVal{Type: xdr.ScValTypeScvMap, Map: &noAmountPtr} + _, err = NewStellarAssetContractEvent(&xdrEvent, passphrase) + require.Error(t, err) +} + func TestSACEventCreation(t *testing.T) { var xdrEvent xdr.ContractEvent resetEvent := func(from string, to string, asset xdr.Asset) { diff --git a/support/contractevents/utils.go b/support/contractevents/utils.go index 2722053bbe..730db08792 100644 --- a/support/contractevents/utils.go +++ b/support/contractevents/utils.go @@ -43,10 +43,29 @@ func parseBalanceChangeEvent(topics xdr.ScVec, value xdr.ScVal) ( return } - amount, ok = value.GetI128() + amount, ok = eventAmount(value) if !ok { - return + return first, second, amount, ErrNotBalanceChangeEvent } return first, second, amount, nil } + +// eventAmount reads the amount from SAC event data. The data is either a bare +// i128, or (CAP-0067, and CAP-0084 for muxed contract destinations) a map +// whose "amount" key holds the i128, alongside keys such as "to_muxed_id". +func eventAmount(value xdr.ScVal) (xdr.Int128Parts, bool) { + if amount, ok := value.GetI128(); ok { + return amount, true + } + entries, ok := value.GetMap() + if !ok || entries == nil { + return xdr.Int128Parts{}, false + } + for _, entry := range *entries { + if key, ok := entry.Key.GetSym(); ok && key == "amount" { + return entry.Val.GetI128() + } + } + return xdr.Int128Parts{}, false +} From 174b61f009bcf74248b15b22941f3e4bd301e3fb Mon Sep 17 00:00:00 2001 From: tamirms Date: Thu, 8 Oct 2026 10:45:25 +0100 Subject: [PATCH 09/10] ingest/ledger: read the signature from every signed StellarValue arm NodeID and Signature read only the SIGNED arm. Read the signature from the CAP-0088 millisecond arms (SIGNED_MS, EMPTY_TX_SET_MS), which every ledger after the Protocol 30 upgrade uses, and from the CAP-0083 EMPTY_TX_SET arm, which was previously missed so empty ledgers returned an error. Based on #5998, rebased onto the stellar-xdr@4f524bba regeneration and renamed to the upstream TimePointMs type. Co-Authored-By: Claude Opus 5.5 --- ingest/CHANGELOG.md | 6 ++++ ingest/ledger/ledger.go | 25 ++++++++++++-- ingest/ledger/ledger_test.go | 65 ++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/ingest/CHANGELOG.md b/ingest/CHANGELOG.md index a2cbb1f979..68924548f4 100644 --- a/ingest/CHANGELOG.md +++ b/ingest/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. This projec ## Pending +### Protocol 30 Support +* `ledger.NodeID` and `ledger.Signature` read the close-value signature from the CAP-0088 millisecond close-time arms (`STELLAR_VALUE_SIGNED_MS`, `STELLAR_VALUE_EMPTY_TX_SET_MS`), which every ledger after the Protocol 30 upgrade uses. Whole-second `CloseTime` is unchanged. + +### Bug Fixes +* `ledger.NodeID` and `ledger.Signature` now also read the signature from the `STELLAR_VALUE_EMPTY_TX_SET` arm (CAP-0083). Ledgers closed with an empty transaction set previously returned an error and an empty signature. + ### Breaking Changes * Removed the `ingest/cdp` pacakge and consolidated components into `github.com/stellar/go-stellar-sdk/ingest`. This affects references to a few components: - `ApplyLedgerMetadata` diff --git a/ingest/ledger/ledger.go b/ingest/ledger/ledger.go index 24b293fb66..6c5a940da3 100644 --- a/ingest/ledger/ledger.go +++ b/ingest/ledger/ledger.go @@ -76,8 +76,29 @@ func TotalByteSizeOfLiveSorobanState(l xdr.LedgerCloseMeta) (uint64, bool) { return 0, false } +// scpValueSignature returns the LedgerCloseValueSignature from whichever +// StellarValue arm carries it: SIGNED, EMPTY_TX_SET (CAP-0083), or their +// millisecond close-time counterparts SIGNED_MS and EMPTY_TX_SET_MS (CAP-0088). +// Only BASIC has no signature. +func scpValueSignature(l xdr.LedgerCloseMeta) (xdr.LedgerCloseValueSignature, bool) { + ext := l.LedgerHeaderHistoryEntry().Header.ScpValue.Ext + if sig, ok := ext.GetLcValueSignature(); ok { + return sig, true + } + if v, ok := ext.GetProposedValue(); ok { + return v.LcValueSignature, true + } + if v, ok := ext.GetSignedMsValue(); ok { + return v.LcValueSignature, true + } + if v, ok := ext.GetProposedMsValue(); ok { + return v.LcValueSignature, true + } + return xdr.LedgerCloseValueSignature{}, false +} + func NodeID(l xdr.LedgerCloseMeta) (string, error) { - LedgerCloseValueSignature, ok := l.LedgerHeaderHistoryEntry().Header.ScpValue.Ext.GetLcValueSignature() + LedgerCloseValueSignature, ok := scpValueSignature(l) if !ok { return "", fmt.Errorf("could not get LedgerCloseValueSignature") @@ -86,7 +107,7 @@ func NodeID(l xdr.LedgerCloseMeta) (string, error) { } func Signature(l xdr.LedgerCloseMeta) (string, bool) { - LedgerCloseValueSignature, ok := l.LedgerHeaderHistoryEntry().Header.ScpValue.Ext.GetLcValueSignature() + LedgerCloseValueSignature, ok := scpValueSignature(l) if !ok { return "", false } diff --git a/ingest/ledger/ledger_test.go b/ingest/ledger/ledger_test.go index e58a2020f0..6e7c02ec49 100644 --- a/ingest/ledger/ledger_test.go +++ b/ingest/ledger/ledger_test.go @@ -59,6 +59,71 @@ func TestLedger(t *testing.T) { assert.Equal(t, uint32(13), total) } +func TestNodeIDAndSignatureMsCloseTime(t *testing.T) { + ledger := ledgerTestInput() + header := &ledger.V1.LedgerHeader.Header + sig := header.ScpValue.Ext.LcValueSignature + header.ScpValue.Ext = xdr.StellarValueExt{ + V: xdr.StellarValueTypeStellarValueSignedMs, + SignedMsValue: &xdr.StellarValueSignedMsValue{ + CloseTimeMs: xdr.TimePointMs(1594584547000), + LcValueSignature: *sig, + }, + } + + nodeID, err := NodeID(ledger) + assert.NoError(t, err) + assert.Equal(t, "GARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA76O", nodeID) + + signature, ok := Signature(ledger) + assert.True(t, ok) + assert.Equal(t, "9g==", signature) + + // Whole-second close time is unchanged by CAP-0088. + assert.Equal(t, int64(1594584547), CloseTime(ledger)) + + // The zero-copy view path rejects unknown ext discriminants, so this fails + // unless xdr_views_generated.go carries the new arms. + raw, err := header.ScpValue.MarshalBinary() + assert.NoError(t, err) + view := xdr.StellarValueView(raw) + assert.NoError(t, view.ValidateFull()) + assert.Equal(t, uint64(1594584547), view.MustCloseTime().MustValue()) + assert.Equal(t, uint64(1594584547000), view.MustExt().MustSignedMsValue().MustCloseTimeMs().MustValue()) +} + +func TestNodeIDAndSignatureEmptyTxSet(t *testing.T) { + ledger := ledgerTestInput() + header := &ledger.V1.LedgerHeader.Header + sig := *header.ScpValue.Ext.LcValueSignature + + for name, ext := range map[string]xdr.StellarValueExt{ + "EMPTY_TX_SET": { + V: xdr.StellarValueTypeStellarValueEmptyTxSet, + ProposedValue: &xdr.StellarValueProposedValue{LcValueSignature: sig}, + }, + "EMPTY_TX_SET_MS": { + V: xdr.StellarValueTypeStellarValueEmptyTxSetMs, + ProposedMsValue: &xdr.StellarValueProposedMsValue{ + CloseTimeMs: xdr.TimePointMs(1594584547000), + LcValueSignature: sig, + }, + }, + } { + t.Run(name, func(t *testing.T) { + header.ScpValue.Ext = ext + + nodeID, err := NodeID(ledger) + assert.NoError(t, err) + assert.Equal(t, "GARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA76O", nodeID) + + signature, ok := Signature(ledger) + assert.True(t, ok) + assert.Equal(t, "9g==", signature) + }) + } +} + func ledgerTestInput() (lcm xdr.LedgerCloseMeta) { lcm = xdr.LedgerCloseMeta{ V: 1, From f981fafd139461462a13aa7952d870505248c535 Mon Sep 17 00:00:00 2001 From: tamirms Date: Wed, 14 Oct 2026 03:57:06 +0100 Subject: [PATCH 10/10] Address review feedback - CHANGELOG: list the contract spec name widening as its own entry, citing stellar/stellar-xdr#312, and add the ContractCredited muxed contract fields - xdr: add StellarValueExt.LedgerCloseValueSignature, which reads the signature from every signed StellarValue arm, and use it in ingest/ledger - strkey: test the SEP-23 muxed contract vectors - processors/token_transfer: test SAC transfer and mint events to a muxed contract Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 5 +- ingest/ledger/ledger.go | 19 +------ .../token_transfer/contract_events_test.go | 35 +++++++++++++ strkey/muxed_contract_test.go | 50 +++++++++++++++++++ xdr/stellar_value.go | 25 ++++++++++ xdr/stellar_value_test.go | 50 +++++++++++++++++++ 6 files changed, 165 insertions(+), 19 deletions(-) create mode 100644 xdr/stellar_value.go create mode 100644 xdr/stellar_value_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index af339ab725..8af57a4887 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,14 +13,17 @@ Official project releases may be found here: https://github.com/stellar/go-stell ## Pending ### Breaking Changes -* xdr: Protocol 30 support (CAP-0084, CAP-0087, CAP-0088). Regenerated from stellar-xdr@4f524bba, where all three CAPs are ungated, so `XDR_FEATURES` is now empty. Contract spec UDT and event names are widened to `SC_SPEC_TYPE_NAME_LIMIT` (1024 bytes), and `ScSpecEventV0.Name` is now a `string` instead of an `ScSymbol`. +* xdr: Protocol 30 support (CAP-0084, CAP-0087, CAP-0088). Regenerated from stellar-xdr@4f524bba, where all three CAPs are ungated, so `XDR_FEATURES` is now empty. +* xdr: Contract spec UDT and event names are widened to `SC_SPEC_TYPE_NAME_LIMIT` (1024 bytes), and `ScSpecEventV0.Name` is now a `string` instead of an `ScSymbol` ([stellar/stellar-xdr#312](https://github.com/stellar/stellar-xdr/pull/312)). This arrives with the same regeneration but is not part of any CAP. * support/storage, historyarchive: Remove `OnDiskCache`, `MakeOnDiskCache`, `ConnectOptions.Wrap`, `ArchiveBucketCache` and `CacheOptions`. They have had no callers since lighthorizon was removed ([#5334](https://github.com/stellar/go-stellar-sdk/pull/5334)) and Horizon moved to fscache ([#5197](https://github.com/stellar/go-stellar-sdk/pull/5197)). An early exit from a bucket read left a partial file in `OnDiskCache` that later reads served as complete ([#6017](https://github.com/stellar/go-stellar-sdk/pull/6017)) ### New Features * xdr: Add the CAP-0084 `ScAddressTypeScAddressTypeMuxedContract` arm of `ScAddress`. `ScAddress.String()` renders it as a `W...` muxed contract strkey. * xdr: Add the CAP-0088 millisecond close-time arms of `StellarValue`: `StellarValueTypeStellarValueSignedMs` (`SignedMsValue`) and `StellarValueTypeStellarValueEmptyTxSetMs` (`ProposedMsValue`), and the `TimePointMs` type. Every ledger after the Protocol 30 upgrade ledger uses one of these arms, so earlier SDK versions cannot decode them. * xdr: Add the CAP-0087 ML-DSA `ContractCostType` values 86–94 (`MlDsa{44,65,87}DecodeVerifyingKey`, `MlDsa{44,65,87}DecodeSignature`, `VerifyMlDsa{44,65,87}Sig`). These are names only; cost params are positional, so they decoded before. +* xdr: Add `StellarValueExt.LedgerCloseValueSignature()`, which returns the close-value signature from whichever `StellarValue` arm carries one: `SIGNED`, `EMPTY_TX_SET` (CAP-0083), `SIGNED_MS` or `EMPTY_TX_SET_MS` (CAP-0088). The generated `GetLcValueSignature()` only reads the `SIGNED` arm, so it finds no signature on any ledger after the Protocol 30 upgrade. * strkey: Add `VersionByteMuxedContract` (`W...`), `MuxedContract`, `DecodeMuxedContract`, and `IsValidMuxedContractAddress`. +* protocols/horizon/effects: Add `ContractMuxed` (`contract_muxed`) and `ContractMuxedID` (`contract_muxed_id`) to `ContractCredited`, set when the credit went to a muxed contract address (CAP-0084). * xdr: Added `LedgerCloseMetaView.LedgerHeader()`, exposing the version-resolving header accessor that already backs `LedgerSequence`, `LedgerCloseTime`, `LedgerHash`, and `PreviousLedgerHash` ([#5982](https://github.com/stellar/go-stellar-sdk/pull/5982)) * xdr: Add `Stream.ResumeFrom`, which positions a new stream at the last record boundary of a failed one and continues its SHA-256 from there, so `ValidateHash` on the new stream covers both ([#6017](https://github.com/stellar/go-stellar-sdk/pull/6017)) * rpcclient: Add `Client.URL()` to expose the configured RPC server URL ([#5885](https://github.com/stellar/go-stellar-sdk/issues/5885)) diff --git a/ingest/ledger/ledger.go b/ingest/ledger/ledger.go index 6c5a940da3..f650d07c93 100644 --- a/ingest/ledger/ledger.go +++ b/ingest/ledger/ledger.go @@ -76,25 +76,8 @@ func TotalByteSizeOfLiveSorobanState(l xdr.LedgerCloseMeta) (uint64, bool) { return 0, false } -// scpValueSignature returns the LedgerCloseValueSignature from whichever -// StellarValue arm carries it: SIGNED, EMPTY_TX_SET (CAP-0083), or their -// millisecond close-time counterparts SIGNED_MS and EMPTY_TX_SET_MS (CAP-0088). -// Only BASIC has no signature. func scpValueSignature(l xdr.LedgerCloseMeta) (xdr.LedgerCloseValueSignature, bool) { - ext := l.LedgerHeaderHistoryEntry().Header.ScpValue.Ext - if sig, ok := ext.GetLcValueSignature(); ok { - return sig, true - } - if v, ok := ext.GetProposedValue(); ok { - return v.LcValueSignature, true - } - if v, ok := ext.GetSignedMsValue(); ok { - return v.LcValueSignature, true - } - if v, ok := ext.GetProposedMsValue(); ok { - return v.LcValueSignature, true - } - return xdr.LedgerCloseValueSignature{}, false + return l.LedgerHeaderHistoryEntry().Header.ScpValue.Ext.LedgerCloseValueSignature() } func NodeID(l xdr.LedgerCloseMeta) (string, error) { diff --git a/processors/token_transfer/contract_events_test.go b/processors/token_transfer/contract_events_test.go index 08a2e27edb..36a38c7c32 100644 --- a/processors/token_transfer/contract_events_test.go +++ b/processors/token_transfer/contract_events_test.go @@ -1885,6 +1885,41 @@ func TestValidContractEventsV4(t *testing.T) { } } +// CAP-0084: the SAC reports a transfer or mint to a muxed contract (W...) the +// same way as one to a muxed account. The "to" topic is the plain contract +// address, and the multiplexing id is to_muxed_id in the data map. +func TestSacEventsToMuxedContractV4(t *testing.T) { + v4Tx := someTxV3() + v4Tx.UnsafeMeta.V = 4 + v4Tx.UnsafeMeta.V4 = &xdr.TransactionMetaV4{ + Operations: []xdr.OperationMetaV2{{}}, + } + asset := createString(xlmAsset.StringCanonical()) + data := createScMap("amount", createInt128(thousand), "to_muxed_id", uint64(7)) + + parse := func(topics ...xdr.ScVal) *TokenTransferEvent { + contractEvent := createContractEventFromTopicsAndData(contractIdFromAsset(xlmAsset), topics, data) + event, err := processor.parseEvent(v4Tx, &someOperationIndex, contractEvent) + require.NoError(t, err) + require.NotNil(t, event) + assert.NotNil(t, event.GetAsset()) + require.NotNil(t, event.Meta.ToMuxedInfo) + assert.Equal(t, uint64(7), event.Meta.ToMuxedInfo.GetId()) + return event + } + + transfer := parse(createSymbol(TransferEvent), createAddress(randomAccount), createAddress(someContract1), asset).GetTransfer() + require.NotNil(t, transfer) + assert.Equal(t, randomAccount, transfer.From) + assert.Equal(t, someContract1, transfer.To) + assert.Equal(t, thousandStr, transfer.Amount) + + mint := parse(createSymbol(MintEvent), createAddress(someContract1), asset).GetMint() + require.NotNil(t, mint) + assert.Equal(t, someContract1, mint.To) + assert.Equal(t, thousandStr, mint.Amount) +} + func TestV4FeeEventParsing(t *testing.T) { // Create V4 transaction v4Tx := someTxV3() diff --git a/strkey/muxed_contract_test.go b/strkey/muxed_contract_test.go index 07470fb39d..ee41193b7f 100644 --- a/strkey/muxed_contract_test.go +++ b/strkey/muxed_contract_test.go @@ -78,6 +78,56 @@ func TestDecodeMuxedContractRejectionReasons(t *testing.T) { assert.Contains(t, err.Error(), "non-canonical") } +// Vectors from the SEP-23 muxed contract addition (stellar/stellar-protocol#2020). +func TestMuxedContractSEP23Vectors(t *testing.T) { + const contract = "CA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUWDA" + hash := []byte{ + 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, + 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, + 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, + 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a, + } + + for _, v := range []struct { + id uint64 + address string + }{ + {0, "WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAAAWWC"}, + // id above the maximum signed 64-bit integer + {9223372036854775808, "WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAACWJY"}, + } { + muxed, err := DecodeMuxedContract(v.address) + require.NoError(t, err, v.address) + assert.Equal(t, v.id, muxed.ID()) + contractBytes := muxed.Contract() + assert.Equal(t, hash, contractBytes[:]) + contractID, err := muxed.ContractID() + require.NoError(t, err) + assert.Equal(t, contract, contractID) + address, err := muxed.Address() + require.NoError(t, err) + assert.Equal(t, v.address, address) + } + + for _, invalid := range []string{ + // the unused trailing bit is not zero + "WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAAAWWD", + // length congruent to 6 mod 8 + "WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAAAWWCA", + // decodes to 44 bytes instead of 43 + "WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAAAAIOUI", + // the low 3 bits of the version byte are 7 + "W47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAADXHW", + // invalid checksum + "WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAAAWWA", + } { + muxed, err := DecodeMuxedContract(invalid) + assert.EqualError(t, err, "invalid muxed contract", invalid) + assert.Nil(t, muxed) + assert.False(t, IsValidMuxedContractAddress(invalid), invalid) + } +} + func TestMuxedContract_SetContractID(t *testing.T) { muxed := MuxedContract{} assert.EqualError(t, muxed.SetContractID(""), "invalid contract address") diff --git a/xdr/stellar_value.go b/xdr/stellar_value.go new file mode 100644 index 0000000000..a598adfea2 --- /dev/null +++ b/xdr/stellar_value.go @@ -0,0 +1,25 @@ +package xdr + +// LedgerCloseValueSignature returns the close-value signature from whichever +// StellarValue arm carries it: SIGNED, EMPTY_TX_SET (CAP-0083), or their +// millisecond close-time counterparts SIGNED_MS and EMPTY_TX_SET_MS +// (CAP-0088). Only BASIC has no signature. +// +// The generated GetLcValueSignature only reads the SIGNED arm, so it finds no +// signature on ledgers closed with an empty transaction set, or on any ledger +// after the Protocol 30 upgrade. +func (e StellarValueExt) LedgerCloseValueSignature() (LedgerCloseValueSignature, bool) { + if sig, ok := e.GetLcValueSignature(); ok { + return sig, true + } + if v, ok := e.GetProposedValue(); ok { + return v.LcValueSignature, true + } + if v, ok := e.GetSignedMsValue(); ok { + return v.LcValueSignature, true + } + if v, ok := e.GetProposedMsValue(); ok { + return v.LcValueSignature, true + } + return LedgerCloseValueSignature{}, false +} diff --git a/xdr/stellar_value_test.go b/xdr/stellar_value_test.go new file mode 100644 index 0000000000..208aae92e9 --- /dev/null +++ b/xdr/stellar_value_test.go @@ -0,0 +1,50 @@ +package xdr + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStellarValueExtLedgerCloseValueSignature(t *testing.T) { + sig := LedgerCloseValueSignature{ + NodeId: NodeId{Type: PublicKeyTypePublicKeyTypeEd25519, Ed25519: &Uint256{1, 2, 3}}, + Signature: Signature{4, 5, 6}, + } + + for name, ext := range map[string]StellarValueExt{ + "SIGNED": { + V: StellarValueTypeStellarValueSigned, + LcValueSignature: &sig, + }, + "EMPTY_TX_SET": { + V: StellarValueTypeStellarValueEmptyTxSet, + ProposedValue: &StellarValueProposedValue{LcValueSignature: sig}, + }, + "SIGNED_MS": { + V: StellarValueTypeStellarValueSignedMs, + SignedMsValue: &StellarValueSignedMsValue{ + CloseTimeMs: TimePointMs(1594584547000), + LcValueSignature: sig, + }, + }, + "EMPTY_TX_SET_MS": { + V: StellarValueTypeStellarValueEmptyTxSetMs, + ProposedMsValue: &StellarValueProposedMsValue{ + CloseTimeMs: TimePointMs(1594584547000), + LcValueSignature: sig, + }, + }, + } { + t.Run(name, func(t *testing.T) { + got, ok := ext.LedgerCloseValueSignature() + assert.True(t, ok) + assert.Equal(t, sig, got) + }) + } + + t.Run("BASIC", func(t *testing.T) { + _, ok := StellarValueExt{V: StellarValueTypeStellarValueBasic}.LedgerCloseValueSignature() + assert.False(t, ok) + }) +}