From 75e427c8cb0b9b86680d09552f20a67be64693cc Mon Sep 17 00:00:00 2001 From: Chris Gianelloni Date: Sun, 7 Dec 2025 12:05:00 -0500 Subject: [PATCH] feat(utxorpc): implement submit.EvalTx Signed-off-by: Chris Gianelloni --- go.mod | 14 +- go.sum | 32 ++- internal/utxorpc/submit.go | 289 +++++++++++++++++++++ openapi/test/api_chainsync_test.go | 3 +- openapi/test/api_default_test.go | 3 +- openapi/test/api_localstatequery_test.go | 3 +- openapi/test/api_localtxmonitor_test.go | 3 +- openapi/test/api_localtxsubmission_test.go | 3 +- 8 files changed, 328 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 69c9239..40297e9 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,9 @@ require ( connectrpc.com/connect v1.19.1 connectrpc.com/grpchealth v1.4.0 connectrpc.com/grpcreflect v1.3.0 - github.com/blinklabs-io/adder v0.34.0 - github.com/blinklabs-io/gouroboros v0.140.0 + github.com/blinklabs-io/adder v0.35.0 + github.com/blinklabs-io/gouroboros v0.142.0 + github.com/blinklabs-io/plutigo v0.0.16 github.com/blinklabs-io/tx-submit-api v0.20.10 github.com/gin-gonic/gin v1.11.0 github.com/gorilla/websocket v1.5.3 @@ -27,19 +28,20 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect - github.com/blinklabs-io/plutigo v0.0.13 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.6 // indirect github.com/btcsuite/btcd/btcutil v1.1.6 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/consensys/gnark-crypto v0.19.1 // indirect + github.com/consensys/gnark-crypto v0.19.2 // indirect github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/ethereum/go-ethereum v1.16.7 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gin-contrib/sse v1.1.0 // indirect @@ -52,6 +54,7 @@ require ( github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.18.0 // indirect + github.com/holiman/uint256 v1.3.2 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -59,6 +62,7 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect diff --git a/go.sum b/go.sum index 765ae93..1c17e09 100644 --- a/go.sum +++ b/go.sum @@ -8,19 +8,21 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/blinklabs-io/adder v0.34.0 h1:QtHzMgfzFO/WnJTAAibEVahLzDNtdyli95+0OD5Jlho= -github.com/blinklabs-io/adder v0.34.0/go.mod h1:3jLrgshUoU4W2r+noPl61nZMlQhUFx6SCVjRyzKMyOQ= -github.com/blinklabs-io/gouroboros v0.140.0 h1:dFK2iunkTflCI2hlDSGZJJ7JerYhpdE41HnBUVvjj9E= -github.com/blinklabs-io/gouroboros v0.140.0/go.mod h1:FxKQNNNRQN5F/Cl4pnW7SHhpPo7fD57mTPGo4N9ulnY= -github.com/blinklabs-io/ouroboros-mock v0.3.9 h1:UnciDccJ5tZCR1xI0BcxGZcYjJ/PS5MpnjiiGtrZ680= -github.com/blinklabs-io/ouroboros-mock v0.3.9/go.mod h1:uTkE8/LAYL7yQSntH48Pudf5Xn+jaBWMj+9udbzYXhI= -github.com/blinklabs-io/plutigo v0.0.13 h1:JztPigFmknQmQ3Ti1+mdTY96ihOUDh6wJ3pPnN2YYBU= -github.com/blinklabs-io/plutigo v0.0.13/go.mod h1:CoNpHHFifPV21KfnHlP3hEFOVj3yF6zgV5OPr058+Do= +github.com/blinklabs-io/adder v0.35.0 h1:QKmXb7HsejqOKxauU2Siw0ekTMqNsFr2cxdLcOQvXWs= +github.com/blinklabs-io/adder v0.35.0/go.mod h1:90EQBbKoIhcCdyJ5OlE9tDT1cn69fW/ePYVatS62BKo= +github.com/blinklabs-io/gouroboros v0.142.0 h1:vBsQfcPsZF7EX+1uw1Gcg0uCYvkjsEVHn2qEqlMC0lY= +github.com/blinklabs-io/gouroboros v0.142.0/go.mod h1:pC42tYVwkd34ohGrWAIHhLOWhhpmrBa0M/QUE+4gC8Q= +github.com/blinklabs-io/ouroboros-mock v0.4.0 h1:ppOcTMnC/2f5rYYSlvNqcGfAQOIpMCSDUrNh9K6a4mY= +github.com/blinklabs-io/ouroboros-mock v0.4.0/go.mod h1:e+Kck8bmdOuaN7IfkbVvbqAVlskXNXB95oHI3YlFG5g= +github.com/blinklabs-io/plutigo v0.0.16 h1:3+1eOby9ckoDXb+8ObCBQ4BGbHZ6z7cxGFDR4cbS+7Y= +github.com/blinklabs-io/plutigo v0.0.16/go.mod h1:aT3mJAh1s8JJ8C42ygd8OyGDQZ8f+w8Uge2+C/9BLug= github.com/blinklabs-io/tx-submit-api v0.20.10 h1:+Il7414Nd4/82pr87tsyYRtE+6j52MGrWh5CIHBJzXc= github.com/blinklabs-io/tx-submit-api v0.20.10/go.mod h1:mqkYyHYrU+XFtbCmiYycYqcxga56JdPLDas0oNTTtsE= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= @@ -29,8 +31,8 @@ github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6 github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU= -github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.6 h1:IzlsEr9olcSRKB/n7c4351F3xHKxS2lma+1UFGCYd4E= +github.com/btcsuite/btcd/btcec/v2 v2.3.6/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= @@ -57,8 +59,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/consensys/gnark-crypto v0.19.1 h1:FWO1JDs7A2OajswzwMG7f8l2Zrxc/yOkxSTByKTc3O0= -github.com/consensys/gnark-crypto v0.19.1/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= +github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= +github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -72,6 +74,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ= +github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= @@ -128,6 +132,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -162,6 +168,8 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/internal/utxorpc/submit.go b/internal/utxorpc/submit.go index 9d82af1..60c27a1 100644 --- a/internal/utxorpc/submit.go +++ b/internal/utxorpc/submit.go @@ -27,6 +27,10 @@ import ( "github.com/blinklabs-io/cardano-node-api/internal/node" "github.com/blinklabs-io/gouroboros/ledger" ocommon "github.com/blinklabs-io/gouroboros/protocol/common" + "github.com/blinklabs-io/plutigo/cek" + "github.com/blinklabs-io/plutigo/data" + "github.com/blinklabs-io/plutigo/syn" + "github.com/utxorpc/go-codegen/utxorpc/v1alpha/cardano" submit "github.com/utxorpc/go-codegen/utxorpc/v1alpha/submit" "github.com/utxorpc/go-codegen/utxorpc/v1alpha/submit/submitconnect" ) @@ -83,6 +87,291 @@ func (s *submitServiceServer) SubmitTx( return connect.NewResponse(resp), nil } +// evaluateScript evaluates a Plutus script with the given arguments +func evaluateScript( + scriptBytes []byte, + args []data.PlutusData, +) (uint64, uint64, error) { + // Decode the script + program, err := syn.Decode[syn.DeBruijn](scriptBytes) + if err != nil { + return 0, 0, fmt.Errorf("decode script: %w", err) + } + + // Apply arguments to the script + term := program.Term + for _, arg := range args { + term = &syn.Apply[syn.DeBruijn]{ + Function: term, + Argument: &syn.Constant{ + Con: &syn.Data{Inner: arg}, + }, + } + } + + // Create machine with version-based cost model + machine := cek.NewMachineWithVersionCosts[syn.DeBruijn]( + program.Version, + 200, + ) + + _, err = machine.Run(term) + if err != nil { + return 0, 0, fmt.Errorf("execute script: %w", err) + } + + // Safe conversion: ExBudget should not be negative, but check to avoid overflow + var steps, memory uint64 + if machine.ExBudget.Cpu < 0 { + steps = 0 + } else { + steps = uint64(machine.ExBudget.Cpu) //nolint:gosec + } + if machine.ExBudget.Mem < 0 { + memory = 0 + } else { + memory = uint64(machine.ExBudget.Mem) //nolint:gosec + } + return steps, memory, nil //nolint:gosec +} + +// convertPlutusData converts plutigo data.PlutusData to *cardano.PlutusData +func convertPlutusData(pd data.PlutusData) *cardano.PlutusData { + switch v := pd.(type) { + case *data.Constr: + fields := make([]*cardano.PlutusData, len(v.Fields)) + for i, field := range v.Fields { + fields[i] = convertPlutusData(field) + } + return &cardano.PlutusData{ + PlutusData: &cardano.PlutusData_Constr{ + Constr: &cardano.Constr{ + Tag: uint32(v.Tag), //nolint:gosec + Fields: fields, + }, + }, + } + case *data.Map: + pairs := make([]*cardano.PlutusDataPair, len(v.Pairs)) + for i, pair := range v.Pairs { + pairs[i] = &cardano.PlutusDataPair{ + Key: convertPlutusData(pair[0]), + Value: convertPlutusData(pair[1]), + } + } + return &cardano.PlutusData{ + PlutusData: &cardano.PlutusData_Map{ + Map: &cardano.PlutusDataMap{ + Pairs: pairs, + }, + }, + } + case *data.Integer: + return &cardano.PlutusData{ + PlutusData: &cardano.PlutusData_BigInt{ + BigInt: &cardano.BigInt{ + BigInt: &cardano.BigInt_Int{ + Int: v.Inner.Int64(), + }, + }, + }, + } + case *data.ByteString: + return &cardano.PlutusData{ + PlutusData: &cardano.PlutusData_BoundedBytes{ + BoundedBytes: v.Inner, + }, + } + case *data.List: + items := make([]*cardano.PlutusData, len(v.Items)) + for i, item := range v.Items { + items[i] = convertPlutusData(item) + } + return &cardano.PlutusData{ + PlutusData: &cardano.PlutusData_Array{ + Array: &cardano.PlutusDataArray{ + Items: items, + }, + }, + } + default: + // Should not happen + return nil + } +} + +// EvalTx +func (s *submitServiceServer) EvalTx( + ctx context.Context, + req *connect.Request[submit.EvalTxRequest], +) (*connect.Response[submit.EvalTxResponse], error) { + // txRaw + txRaw := req.Msg.GetTx() // *AnyChainTx + if txRaw == nil { + return nil, errors.New("transaction is required") + } + log.Printf("Got an EvalTx request") + + resp := &submit.EvalTxResponse{} + + // Connect to node + oConn, err := node.GetConnection(nil) + if err != nil { + return connect.NewResponse(resp), err + } + defer func() { + // Close Ouroboros connection + oConn.Close() + }() + + // Parse the transaction + txRawBytes := txRaw.GetRaw() // raw bytes + txType, err := ledger.DetermineTransactionType(txRawBytes) + if err != nil { + return connect.NewResponse(resp), err + } + tx, err := ledger.NewTransactionFromCbor(txType, txRawBytes) + if err != nil { + return connect.NewResponse(resp), err + } + + // Get protocol parameters for cost models + oConn.LocalStateQuery().Client.Start() + protoParams, err := oConn.LocalStateQuery().Client.GetCurrentProtocolParams() + if err != nil { + return connect.NewResponse(resp), err + } + + // Get witnesses + witnesses := tx.Witnesses() + if witnesses == nil { + return connect.NewResponse( + resp, + ), errors.New( + "transaction witnesses are nil", + ) + } + redeemers := witnesses.Redeemers() + if redeemers == nil { + return connect.NewResponse( + resp, + ), errors.New( + "transaction redeemers are nil", + ) + } + + // Get cost models from protocol parameters + var costModels map[uint][]int64 + if conwayParams, ok := protoParams.(*ledger.ConwayProtocolParameters); ok { + costModels = conwayParams.CostModels + } + + // Get scripts + v1Scripts := witnesses.PlutusV1Scripts() + v2Scripts := witnesses.PlutusV2Scripts() + v3Scripts := witnesses.PlutusV3Scripts() + allScripts := make( + [][]byte, + 0, + len(v1Scripts)+len(v2Scripts)+len(v3Scripts), + ) + scriptVersions := make( + []uint, + 0, + len(v1Scripts)+len(v2Scripts)+len(v3Scripts), + ) + for _, s := range v1Scripts { + allScripts = append(allScripts, s.RawScriptBytes()) + scriptVersions = append(scriptVersions, 1) + } + for _, s := range v2Scripts { + allScripts = append(allScripts, s.RawScriptBytes()) + scriptVersions = append(scriptVersions, 2) + } + for _, s := range v3Scripts { + allScripts = append(allScripts, s.RawScriptBytes()) + scriptVersions = append(scriptVersions, 3) + } + + // Count redeemers for pre-allocation + redeemerCount := 0 + for range redeemers.Iter() { + redeemerCount++ + } + + txEvalRedeemers := make([]*cardano.Redeemer, 0, redeemerCount) + for key, value := range redeemers.Iter() { + redeemer := &cardano.Redeemer{ + Purpose: cardano.RedeemerPurpose(key.Tag), + Payload: convertPlutusData(value.Data.Data), + Index: key.Index, + ExUnits: &cardano.ExUnits{ + Steps: 0, + Memory: 0, + }, + } + + // Try to evaluate if we have a script for this redeemer + if int(key.Index) < len(allScripts) { + scriptBytes := allScripts[key.Index] + version := scriptVersions[key.Index] + + // Build arguments based on purpose + var args []data.PlutusData + + // For now, only handle spending scripts with datum, redeemer, context + if key.Tag == 0 { // RedeemerTagSpend + // Get datum - for simplicity, assume it's the redeemer data for now + datum := value.Data.Data + redeemerData := value.Data.Data + // TODO: build proper script context + contextData := data.NewConstr(0) // dummy context + + args = []data.PlutusData{datum, redeemerData, contextData} + } else { + // For other purposes, just redeemer and context + redeemerData := value.Data.Data + contextData := data.NewConstr(0) // dummy context + args = []data.PlutusData{redeemerData, contextData} + } + + // Filter cost model for this version + versionCostModel := make(map[uint][]int64) + if model, ok := costModels[version]; ok { + versionCostModel[version] = model + } + + steps, memory, err := evaluateScript( + scriptBytes, + args, + ) + if err != nil { + log.Printf( + "Failed to evaluate script for redeemer %d: %v", + key.Index, + err, + ) + } else { + redeemer.ExUnits.Steps = steps + redeemer.ExUnits.Memory = memory + } + } + + txEvalRedeemers = append(txEvalRedeemers, redeemer) + } + + resp.Report = &submit.AnyChainEval{ + Chain: &submit.AnyChainEval_Cardano{ + Cardano: &cardano.TxEval{ + Fee: nil, // TODO: set fee + Redeemers: txEvalRedeemers, + }, + }, + } + + return connect.NewResponse(resp), nil +} + func (s *submitServiceServer) WaitForTx( ctx context.Context, req *connect.Request[submit.WaitForTxRequest], diff --git a/openapi/test/api_chainsync_test.go b/openapi/test/api_chainsync_test.go index 3c016b6..c76a2d8 100644 --- a/openapi/test/api_chainsync_test.go +++ b/openapi/test/api_chainsync_test.go @@ -11,10 +11,11 @@ package openapi import ( "context" + "testing" + openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_openapi_ChainsyncAPIService(t *testing.T) { diff --git a/openapi/test/api_default_test.go b/openapi/test/api_default_test.go index 488ef33..0a895d7 100644 --- a/openapi/test/api_default_test.go +++ b/openapi/test/api_default_test.go @@ -11,10 +11,11 @@ package openapi import ( "context" + "testing" + openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_openapi_DefaultAPIService(t *testing.T) { diff --git a/openapi/test/api_localstatequery_test.go b/openapi/test/api_localstatequery_test.go index 6c4d208..a1c0b25 100644 --- a/openapi/test/api_localstatequery_test.go +++ b/openapi/test/api_localstatequery_test.go @@ -11,10 +11,11 @@ package openapi import ( "context" + "testing" + openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_openapi_LocalstatequeryAPIService(t *testing.T) { diff --git a/openapi/test/api_localtxmonitor_test.go b/openapi/test/api_localtxmonitor_test.go index c8e9106..16c1a64 100644 --- a/openapi/test/api_localtxmonitor_test.go +++ b/openapi/test/api_localtxmonitor_test.go @@ -11,10 +11,11 @@ package openapi import ( "context" + "testing" + openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_openapi_LocaltxmonitorAPIService(t *testing.T) { diff --git a/openapi/test/api_localtxsubmission_test.go b/openapi/test/api_localtxsubmission_test.go index 46a5865..b30c257 100644 --- a/openapi/test/api_localtxsubmission_test.go +++ b/openapi/test/api_localtxsubmission_test.go @@ -11,10 +11,11 @@ package openapi import ( "context" + "testing" + openapiclient "github.com/blinklabs-io/cardano-node-api/openapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_openapi_LocaltxsubmissionAPIService(t *testing.T) {