From b3c43fa5778e84fa58deab5d5217bbc5ade2088e Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Sun, 15 Sep 2024 13:37:23 -0500 Subject: [PATCH] feat: Conway era support (#265) * use development version of apollo with Conway support * create apollo ChainContext to support reading script ref UTxOs from storage * add tuna-v2-test profile for preview testing --- go.mod | 6 ++-- go.sum | 12 +++---- internal/config/profiles.go | 38 +++++++++++++++++++++++ internal/indexer/indexer.go | 25 +++++++++------ internal/storage/storage.go | 17 ++++++++++ internal/tx/context.go | 62 +++++++++++++++++++++++++++++++++++++ internal/tx/tx.go | 2 +- 7 files changed, 143 insertions(+), 19 deletions(-) create mode 100644 internal/tx/context.go diff --git a/go.mod b/go.mod index 208fa3c..ac1b8a4 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/blinklabs-io/bluefin go 1.22 require ( - github.com/Salvionied/apollo v1.0.12 + github.com/Salvionied/apollo v1.0.13-0.20240908221422-e50b26fff03e github.com/blinklabs-io/adder v0.23.2 github.com/blinklabs-io/bursa v0.8.2 github.com/blinklabs-io/cardano-models v0.3.6 - github.com/blinklabs-io/gouroboros v0.93.2 + github.com/blinklabs-io/gouroboros v0.93.3 github.com/blinklabs-io/merkle-patricia-forestry v0.1.0 github.com/dgraph-io/badger/v4 v4.3.0 github.com/kelseyhightower/envconfig v1.4.0 @@ -51,7 +51,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/maestro-org/go-sdk v1.1.3 // indirect + github.com/maestro-org/go-sdk v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/utxorpc/go-codegen v0.9.0 // indirect diff --git a/go.sum b/go.sum index 4e9e225..e578c0e 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Salvionied/apollo v1.0.12 h1:kC12bSqOQX7cZjKh9D4vqBX/VH9jZd9z+hPXdLZTalg= -github.com/Salvionied/apollo v1.0.12/go.mod h1:BLs3iWs5ovGcVqFvpuX3g0RmLB9QIYMwFgzcN9rhkGA= +github.com/Salvionied/apollo v1.0.13-0.20240908221422-e50b26fff03e h1:Pvo1XJK30LAiZKAhiGhroKiLWaLPghfUEWzXNIsWsmo= +github.com/Salvionied/apollo v1.0.13-0.20240908221422-e50b26fff03e/go.mod h1:lOk22W7FRdqg107bHMo8K68P0WIoDejbd9TQpXRLI20= github.com/Salvionied/cbor/v2 v2.6.0 h1:OEwlZLiodLdNeM9wFoSydLvj6/rHRaxu5G8VzwXSeuY= github.com/Salvionied/cbor/v2 v2.6.0/go.mod h1:oFxaUo/mQ5sG1k459nzctGdYa80jy0ZqZ9pln9C/fGw= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -13,8 +13,8 @@ github.com/blinklabs-io/bursa v0.8.2 h1:f4ofPhTCW/Kev5lMrHRqf37s91NrUeVrpPvL4+JW github.com/blinklabs-io/bursa v0.8.2/go.mod h1:gwpOJC25ASg+xL5lF8nHGMA0Aqv3vt41tGUDiruFjew= github.com/blinklabs-io/cardano-models v0.3.6 h1:EmupOempdGjfsvOuItaCZ8FlMiwbF1NBiztqfa3HB1I= github.com/blinklabs-io/cardano-models v0.3.6/go.mod h1:QVU0hGMQDlM+uPFgibGJdz2aQY98QvL2/HCoXGcWVyE= -github.com/blinklabs-io/gouroboros v0.93.2 h1:Rtm6FucK/F4/x1tjMg0ur8Xqbm6VpLInL8JtEsNoAwY= -github.com/blinklabs-io/gouroboros v0.93.2/go.mod h1:svGW4teG78s5urbaYy7StYsVUizNkghW7GYxN9AkANY= +github.com/blinklabs-io/gouroboros v0.93.3 h1:VErIFwZYXJBNrFwJF1HOcIzpHu/ID1PPg4aOF4GPvZw= +github.com/blinklabs-io/gouroboros v0.93.3/go.mod h1:lfvV4sV5tNz/qkaLiR85pKpKqPlHfAa5wFhWGbgsXZ0= github.com/blinklabs-io/merkle-patricia-forestry v0.1.0 h1:tpnFc3uhxm1g14aOd2WsGUJc8wJS1WsVe6tCDZqjGsI= github.com/blinklabs-io/merkle-patricia-forestry v0.1.0/go.mod h1:0RVrI9p0itJdOGr35s3c8uGjO6iUkQ5dlZFsu8IoFYk= github.com/blinklabs-io/ouroboros-mock v0.3.3 h1:c6jN9qcLzNQSVh3zjPE61gF33UkkRRIiNqSGBkZ10cY= @@ -118,8 +118,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/maestro-org/go-sdk v1.1.3 h1:ORkeu1NXesRkdW7Pr5N956GojCibmGMdjXuO5jNAOZk= -github.com/maestro-org/go-sdk v1.1.3/go.mod h1:EYaRwFT8nkwFzZsN6xK256j+r7ASUUn9p44RlaqYjE8= +github.com/maestro-org/go-sdk v1.2.0 h1:0YbC5bwRWklV25L1Z4p2pg2VQYasZGDaC9Epfxwd3a0= +github.com/maestro-org/go-sdk v1.2.0/go.mod h1:EYaRwFT8nkwFzZsN6xK256j+r7ASUUn9p44RlaqYjE8= 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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/internal/config/profiles.go b/internal/config/profiles.go index 21559de..7fcc657 100644 --- a/internal/config/profiles.go +++ b/internal/config/profiles.go @@ -83,6 +83,44 @@ var Profiles = map[string]map[string]Profile{ "00000d1c0cc8af18e5e69d1f43196f528af8b2b08d52467376b657b7232172a3", }, }, + "tuna-v2-test": Profile{ + UseTunaV1: false, + // NOTE: this intercept point corresponds to the block before the script input ref UTxO(s) below + InterceptHash: "d85cb1e85ac4f4b14b8f576dec0bbcdd6fc85eb1db4180357f9935bf8715bd60", + InterceptSlot: 55194547, + ScriptAddress: "addr_test1wqshm8qryrhpjlf0trsnag8ujcpl3js20qd7qdf8vad7qfsyphndm", + ValidatorHash: "217d9c0320ee197d2f58e13ea0fc9603f8ca0a781be03527675be026", + MintValidatorHash: "40c8fc6abc73927132613dfdb473bb5d49484f50d78fa399cc63a12e", + ScriptRefInputs: []RefInput{ + // Spend script + { + TxId: "2eb62193510eb76c153ad0f56bb6e0207b4244c671cbbd6ef4e7f847cc5ae694", + OutputIdx: 1, + }, + // Mint script + { + TxId: "9c57fd563ff50919dc6c1cea0aa68044137e4a849cd0d91ac825e83de32c458d", + OutputIdx: 0, + }, + }, + // NOTE: these come from the upstream V1PreviewHistory.json file + // https://github.com/cardano-miners/fortuna/blob/df7b5d6c75ae334529b29b6cb00f3dfbddf762de/V1PreviewHistory.json + SeedHashes: []string{ + "8a623238dcfe41cd25356cac234e202b8cd3a7e9fe295e462818c610e4b82a8d", + "00000c4708deb49a1076a185b4ab963ea7107ee08fd94bbe12ad7f8072a210c4", + "00000f3390355d958e16eaf7252f70058fe19cebb0b1a7f940e2ce18021d8bfe", + "000002694ef5552134bb700fe031c6591bd4b1d81c1bb4a117b635662953d039", + "00000a252051028bd77ab25139419ed03254ba24a8332479176c4df8005d7336", + "000002fbe7b31cad029b21e69ee39227029be382c8a6f2338405d0b1bf681eaf", + "00000769debb1fe2576c1c81037073754dadf18431519bd2fab3a2d8abad1ae7", + "00000e4ed1ded7f0336ec69e01310a1e4b73eaad1f4a900b7c99d30448c5cfee", + "000004fd95295374da6c85e69a81d03e5b50a1bd9694ff68b983b7561d346405", + "0000008b29b3a26868f65c6c046c7a115aea7cbf48bec9f9474952bb51536230", + "000002e9a6dcae68fc1cc22109d7ca363b4435749641601ff7c3e09715c2c5eb", + "00000c7383ee4c191a32b84e2ec5ec200241a57cc4f58ee1d65cab645973003c", + "00000b7fdf94f608cd61fc4620e9575d163461d2dd59f5bb39300e11e6fec200", + }, + }, "tuna-v2-short-epoch": Profile{ UseTunaV1: false, EpochNumber: 50, diff --git a/internal/indexer/indexer.go b/internal/indexer/indexer.go index ce05141..02aedff 100644 --- a/internal/indexer/indexer.go +++ b/internal/indexer/indexer.go @@ -27,7 +27,6 @@ import ( "github.com/blinklabs-io/bluefin/internal/wallet" "github.com/blinklabs-io/adder/event" - filter_chainsync "github.com/blinklabs-io/adder/filter/chainsync" filter_event "github.com/blinklabs-io/adder/filter/event" input_chainsync "github.com/blinklabs-io/adder/input/chainsync" output_embedded "github.com/blinklabs-io/adder/output/embedded" @@ -60,7 +59,6 @@ var globalIndexer = &Indexer{} func (i *Indexer) Start() error { cfg := config.GetConfig() profileCfg := config.GetProfile() - bursa := wallet.GetWallet() // Load saved block data var lastBlockDataBytes cbor.RawMessage if err := storage.GetStorage().GetBlockData(&(lastBlockDataBytes)); err != nil { @@ -153,13 +151,6 @@ func (i *Indexer) Start() error { filter_event.WithTypes([]string{"chainsync.transaction", "chainsync.rollback"}), ) i.pipeline.AddFilter(filterEvent) - // We only care about transactions on a certain address - filterChainsync := filter_chainsync.New( - filter_chainsync.WithAddresses( - []string{cfg.Indexer.ScriptAddress, bursa.PaymentAddress}, - ), - ) - i.pipeline.AddFilter(filterChainsync) // Configure pipeline output output := output_embedded.New( output_embedded.WithCallbackFunc(i.handleEvent), @@ -258,6 +249,22 @@ func (i *Indexer) handleEventTransaction(evt event.Event) error { // Process produced UTxOs startMiner := false for _, utxo := range eventTx.Transaction.Produced() { + // Check for reference inputs + for _, refInput := range profileCfg.ScriptRefInputs { + if refInput.TxId == eventCtx.TransactionHash && + refInput.OutputIdx == utxo.Id.Index() { + // Record script ref UTxO + if err := store.AddUtxo( + "script_ref", + eventCtx.TransactionHash, + utxo.Id.Index(), + utxo.Output.Cbor(), + eventCtx.SlotNumber, + ); err != nil { + return err + } + } + } outputAddress := utxo.Output.Address().String() // Ignore outputs to addresses that we don't care about if outputAddress != cfg.Indexer.ScriptAddress && diff --git a/internal/storage/storage.go b/internal/storage/storage.go index fcaf1b0..203e9e5 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -269,6 +269,23 @@ func (s *Storage) RemoveUtxo( return nil } +func (s *Storage) GetScriptRefUtxo(txId string, outputIdx int) ([]byte, error) { + var ret []byte + key := []byte(fmt.Sprintf("utxo_script_ref_%s.%d", txId, outputIdx)) + err := s.db.View(func(txn *badger.Txn) error { + item, err := txn.Get(key) + if err != nil { + return err + } + ret, err = item.ValueCopy(nil) + return err + }) + if err != nil { + return nil, err + } + return ret, nil +} + func (s *Storage) GetUtxos(address string) ([][]byte, error) { var ret [][]byte keyPrefix := []byte(fmt.Sprintf("utxo_%s_", address)) diff --git a/internal/tx/context.go b/internal/tx/context.go new file mode 100644 index 0000000..10bc846 --- /dev/null +++ b/internal/tx/context.go @@ -0,0 +1,62 @@ +// Copyright 2024 Blink Labs Software +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tx + +import ( + "fmt" + "log/slog" + "os" + + "github.com/Salvionied/apollo/serialization/UTxO" + "github.com/Salvionied/apollo/txBuilding/Backend/Base" + "github.com/Salvionied/apollo/txBuilding/Backend/FixedChainContext" + "github.com/blinklabs-io/bluefin/internal/storage" + "github.com/blinklabs-io/gouroboros/cbor" +) + +// CustomChainContext allows Apollo to lookup script ref UTxOs from our storage +type CustomChainContext struct { + Base.ChainContext +} + +func NewCustomChainContext() CustomChainContext { + return CustomChainContext{ + ChainContext: FixedChainContext.InitFixedChainContext(), + } +} + +func (c CustomChainContext) GetUtxoFromRef(txHash string, txIndex int) *UTxO.UTxO { + var ret UTxO.UTxO + store := storage.GetStorage() + store.Lock() + utxoBytes, err := store.GetScriptRefUtxo(txHash, txIndex) + if err != nil { + slog.Error( + fmt.Sprintf("failed to get script ref UTxO: %s", err), + ) + slog.Warn( + "NOTE: this probably means that you need to remove your .bluefin directory to re-sync from scratch", + ) + os.Exit(1) + } + store.Unlock() + if _, err := cbor.Decode(utxoBytes, &ret); err != nil { + slog.Error( + fmt.Sprintf("failed to decode script ref UTxO bytes: %s", err), + ) + os.Exit(1) + } + return &ret +} diff --git a/internal/tx/tx.go b/internal/tx/tx.go index 6b5aeea..cf9c0ac 100644 --- a/internal/tx/tx.go +++ b/internal/tx/tx.go @@ -91,7 +91,7 @@ func createTx(blockData any, nonce [16]byte) ([]byte, error) { contractAddress, _ := serAddress.DecodeAddress(cfg.Indexer.ScriptAddress) myAddress, _ := serAddress.DecodeAddress(bursa.PaymentAddress) - cc := apollo.NewEmptyBackend() + cc := NewCustomChainContext() apollob := apollo.New(&cc) apollob = apollob. SetWalletFromBech32(bursa.PaymentAddress).