From 916886a453669a9f9c4c0ce23fe6413318bf4c54 Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 22 Dec 2024 15:53:55 +0000 Subject: [PATCH 1/9] Changes to core structures to accommodate Flow conventions --- cmd/metalo/actions/accounts.go | 2 +- cmd/metalo/operations/export_ledger.go | 6 ++--- cmd/metalo/operations/import_ledger.go | 4 +-- index/bolt/index.go | 6 ++--- index/interface.go | 10 +++---- index/root.go | 4 +-- ledger/local/ledger.go | 36 +++++++++++++------------- ledger/local/ledger_test.go | 2 +- model/account/account.go | 4 +-- model/block.go | 2 +- model/dataset.go | 2 +- model/dataset/dataset.go | 8 +++--- model/dataset/future.go | 2 +- model/dataset/wait.go | 6 ++--- model/ledger.go | 8 +++--- model/locker.go | 14 +++++----- model/locker_test.go | 2 +- model/raters/prv21/interface.go | 24 ++++++++--------- model/raters/prv21/rater.go | 6 ++--- model/raters/prv21/rater_test.go | 26 +++++++++---------- model/record.go | 2 +- model/scanner/scanner.go | 34 ++++++++++++------------ model/scanner/scanner_test.go | 2 +- model/scanner/subscription.go | 12 ++++----- node/api/ledger.go | 6 ++--- node/api/status.go | 2 +- remote/caller/blocks.go | 6 ++--- remote/caller/types.go | 2 +- remote/factory.go | 2 +- sdk/testbase/dataset.go | 6 ++--- utils/type_conversion.go | 9 +++++++ wallet/datastore.go | 8 +++--- wallet/index_updater.go | 2 +- wallet/wallet.go | 2 +- 34 files changed, 139 insertions(+), 130 deletions(-) diff --git a/cmd/metalo/actions/accounts.go b/cmd/metalo/actions/accounts.go index 5e2f1ed..7ada592 100644 --- a/cmd/metalo/actions/accounts.go +++ b/cmd/metalo/actions/accounts.go @@ -40,7 +40,7 @@ import ( "github.com/urfave/cli/v2" ) -func createPersona(name, idType string, firstBlock int64) (*account.Identity, error) { +func createPersona(name, idType string, firstBlock uint64) (*account.Identity, error) { did, err := model.GenerateDID() if err != nil { return nil, err diff --git a/cmd/metalo/operations/export_ledger.go b/cmd/metalo/operations/export_ledger.go index 6087355..7cf959e 100644 --- a/cmd/metalo/operations/export_ledger.go +++ b/cmd/metalo/operations/export_ledger.go @@ -35,7 +35,7 @@ func ExportLedger(ctx context.Context, ledger model.Ledger, offChainStorage mode if err != nil { return err } - log.Info().Int64("number", tb.Number).Msg("Top block") + log.Info().Uint64("number", tb.Number).Msg("Top block") currentBlock := gb.Number blockBatchSize := 10 @@ -83,9 +83,9 @@ func ExportLedger(ctx context.Context, ledger model.Ledger, offChainStorage mode } func SaveBlock(ctx context.Context, ledger model.Ledger, offChainStorage model.OffChainStorage, basePath string, b *model.Block) error { - log.Info().Int64("number", b.Number).Msg("Saving block") + log.Info().Uint64("number", b.Number).Msg("Saving block") - dest := path.Join(basePath, utils.Int64ToString(b.Number)) + dest := path.Join(basePath, utils.Uint64ToString(b.Number)) err := os.MkdirAll(dest, 0o700) if err != nil { return err diff --git a/cmd/metalo/operations/import_ledger.go b/cmd/metalo/operations/import_ledger.go index ba71912..f8dec51 100644 --- a/cmd/metalo/operations/import_ledger.go +++ b/cmd/metalo/operations/import_ledger.go @@ -42,9 +42,9 @@ func ImportLedger(ctx context.Context, ledger model.Ledger, offChainStorage mode return err } - var i int64 + var i uint64 for i = 0; i <= tb.Number; i++ { - blockPath := path.Join(dest, utils.Int64ToString(i)) + blockPath := path.Join(dest, utils.Uint64ToString(i)) if importOperations { if err := filepath.Walk(path.Join(blockPath, "operations"), func(filePath string, f os.FileInfo, err error) error { diff --git a/index/bolt/index.go b/index/bolt/index.go index 46b453c..6243f31 100644 --- a/index/bolt/index.go +++ b/index/bolt/index.go @@ -74,7 +74,7 @@ func (dwi *Index) indexBucket(tx *bbolt.Tx, bucketID string) *bbolt.Bucket { return b } -func (dwi *Index) AddLease(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64) error { +func (dwi *Index) AddLease(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64) error { defer measure.ExecTime("index.AddLease")() r := ds.Record() @@ -397,7 +397,7 @@ func (dwi *Index) AddLeaseRevocation(ctx context.Context, ds model.DataSet) erro return nil } -func (dwi *Index) UpdateTopBlock(ctx context.Context, blockNumber int64) error { +func (dwi *Index) UpdateTopBlock(ctx context.Context, blockNumber uint64) error { if blockNumber <= 0 { return errors.New("no block ID provided when updating locker stats " + "(maybe there were no new blocks processed?)") @@ -749,7 +749,7 @@ func (dwi *Index) LockerStates(ctx context.Context) ([]index.LockerState, error) return states, nil } -func (dwi *Index) AddLockerState(ctx context.Context, accountID, lockerID string, firstBlock int64) error { +func (dwi *Index) AddLockerState(ctx context.Context, accountID, lockerID string, firstBlock uint64) error { defer measure.ExecTime("index.AddLockerState")() found := false diff --git a/index/interface.go b/index/interface.go index 4146474..6569e3c 100644 --- a/index/interface.go +++ b/index/interface.go @@ -45,8 +45,8 @@ type ( ID string `json:"id"` IndexID string `json:"indexID"` AccountID string `json:"accountID"` - FirstBlock int64 `json:"firstBlock,omitempty"` - TopBlock int64 `json:"topBlock,omitempty"` + FirstBlock uint64 `json:"firstBlock,omitempty"` + TopBlock uint64 `json:"topBlock,omitempty"` } Properties struct { @@ -81,10 +81,10 @@ type ( ID() string LockerStates(ctx context.Context) ([]LockerState, error) - AddLockerState(ctx context.Context, accountID, lockerID string, firstBlock int64) error - AddLease(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64) error + AddLockerState(ctx context.Context, accountID, lockerID string, firstBlock uint64) error + AddLease(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64) error AddLeaseRevocation(ctx context.Context, ds model.DataSet) error - UpdateTopBlock(ctx context.Context, blockNumber int64) error + UpdateTopBlock(ctx context.Context, blockNumber uint64) error } StoreProperties struct { diff --git a/index/root.go b/index/root.go index 4122a6d..bdacb19 100644 --- a/index/root.go +++ b/index/root.go @@ -34,7 +34,7 @@ type ( Status model.RecordStatus `json:"status"` LockerID string `json:"locker"` ParticipantID string `json:"participant"` - BlockNumber int64 `json:"blockNumber"` + BlockNumber uint64 `json:"blockNumber"` Index uint32 `json:"index"` ImpressionID string `json:"impression,omitempty"` ContentType string `json:"contentType,omitempty"` @@ -55,7 +55,7 @@ type ( Status model.RecordStatus `json:"status"` LockerID string `json:"locker"` ParticipantID string `json:"participant"` - BlockNumber int64 `json:"block"` + BlockNumber uint64 `json:"block"` Index uint32 `json:"index"` AssetID string `json:"asset"` ImpressionID string `json:"imp"` diff --git a/ledger/local/ledger.go b/ledger/local/ledger.go index 3c0c8e0..574eab6 100644 --- a/ledger/local/ledger.go +++ b/ledger/local/ledger.go @@ -62,7 +62,7 @@ type BoltLedger struct { var _ model.Ledger = (*BoltLedger)(nil) type LocalBlock struct { - Number int64 `json:"number"` + Number uint64 `json:"number"` Hash string `json:"hash"` ParentHash string `json:"parentHash,omitempty"` Nonce string `json:"nonce,omitempty"` @@ -226,8 +226,8 @@ func (bl *BoltLedger) GetRecordState(ctx context.Context, rid string) (*model.Re } } -func (bl *BoltLedger) GetBlock(ctx context.Context, bn int64) (*model.Block, error) { - blockKey := utils.Int64ToString(bn) +func (bl *BoltLedger) GetBlock(ctx context.Context, bn uint64) (*model.Block, error) { + blockKey := utils.Uint64ToString(bn) b, err := bl.client.FetchBytes(BlocksKey, blockKey) if err != nil { return nil, err @@ -245,8 +245,8 @@ func (bl *BoltLedger) GetBlock(ctx context.Context, bn int64) (*model.Block, err return &bb, nil } -func (bl *BoltLedger) GetBlockRecords(ctx context.Context, bn int64) ([][]string, error) { - blockKey := utils.Int64ToString(bn) +func (bl *BoltLedger) GetBlockRecords(ctx context.Context, bn uint64) ([][]string, error) { + blockKey := utils.Uint64ToString(bn) resMap := make(map[int][]string) err := bl.client.DB.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BlockCompositionsKey)) @@ -255,7 +255,7 @@ func (bl *BoltLedger) GetBlockRecords(ctx context.Context, bn int64) ([][]string } b = b.Bucket([]byte(blockKey)) if b == nil { - log.Warn().Int64("number", bn).Msg("Block composition not found") + log.Warn().Uint64("number", bn).Msg("Block composition not found") return nil } @@ -326,18 +326,18 @@ func (bl *BoltLedger) GetTopBlock(ctx context.Context) (*model.Block, error) { return &bb, nil } -func (bl *BoltLedger) GetChain(ctx context.Context, startNumber int64, depth int) ([]*model.Block, error) { +func (bl *BoltLedger) GetChain(ctx context.Context, startNumber uint64, depth int) ([]*model.Block, error) { result := make([]*model.Block, 0) - var i int64 = 0 + var i uint64 = 0 err := bl.client.DB.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BlocksKey)) if b == nil { return fmt.Errorf("bucket %s not found", BlocksKey) } - for ; i < int64(depth); i++ { + for ; i < uint64(depth); i++ { - val := b.Get([]byte(utils.Int64ToString(startNumber + i))) + val := b.Get([]byte(utils.Uint64ToString(startNumber + i))) if val == nil { break @@ -502,7 +502,7 @@ func (bl *BoltLedger) OpenNewBlockSession() (string, error) { return curSessionID, nil } -func (bl *BoltLedger) updateRecordState(tx *bbolt.Tx, rid string, status model.RecordStatus, blockNumber int64) error { +func (bl *BoltLedger) updateRecordState(tx *bbolt.Tx, rid string, status model.RecordStatus, blockNumber uint64) error { b := tx.Bucket([]byte(RecordStatesKey)) if b == nil { return fmt.Errorf("bucket %s not found", RecordStatesKey) @@ -525,7 +525,7 @@ func (bl *BoltLedger) updateRecordState(tx *bbolt.Tx, rid string, status model.R } rs.Status = status - if blockNumber != -1 { + if blockNumber != 0 { rs.BlockNumber = blockNumber } @@ -606,9 +606,9 @@ func (bl *BoltLedger) SubmitNewBlock(block *model.Block, records []*model.Record return err } - log.Info().Int64("number", block.Number).Msg("----==== NEW BLOCK ====----") + log.Info().Uint64("number", block.Number).Msg("----==== NEW BLOCK ====----") - blockKey := utils.Int64ToString(block.Number) + blockKey := utils.Uint64ToString(block.Number) if err := bl.client.DB.Update(func(tx *bbolt.Tx) error { if err = bl.client.UpdateInline(tx, BlocksKey, blockKey, bb); err != nil { @@ -663,8 +663,8 @@ func (bl *BoltLedger) SubmitNewBlock(block *model.Block, records []*model.Record } // apply revocations - if err = bl.updateRecordState(tx, rec.SubjectRecord, model.StatusRevoked, -1); err != nil { - if err := bl.updateRecordState(tx, rec.ID, model.StatusFailed, -1); err != nil { + if err = bl.updateRecordState(tx, rec.SubjectRecord, model.StatusRevoked, 0); err != nil { + if err := bl.updateRecordState(tx, rec.ID, model.StatusFailed, 0); err != nil { log.Err(err).Str("rid", rec.ID).Msg("Error when setting record status as failed") } continue @@ -703,7 +703,7 @@ func (bl *BoltLedger) SubmitNewBlock(block *model.Block, records []*model.Record } if prevHeadRecordID != "" { - if err = bl.updateRecordState(tx, prevHeadRecordID, model.StatusRevoked, -1); err != nil { + if err = bl.updateRecordState(tx, prevHeadRecordID, model.StatusRevoked, 0); err != nil { return err } } @@ -885,7 +885,7 @@ func generateNewBlock(ctx context.Context, bl *BoltLedger, seed string) error { // generate new block - var number int64 = 0 + var number uint64 = 0 if prevBlock != nil { prevBlockHash = prevBlock.Hash number = prevBlock.Number + 1 diff --git a/ledger/local/ledger_test.go b/ledger/local/ledger_test.go index e3a2d77..613967d 100644 --- a/ledger/local/ledger_test.go +++ b/ledger/local/ledger_test.go @@ -100,5 +100,5 @@ func TestBoltLedger_SaveRecord(t *testing.T) { rs, err := bl.GetRecordState(ctx, "xx") require.NoError(t, err) assert.Equal(t, model.StatusPending, rs.Status) - assert.Equal(t, int64(0), rs.BlockNumber) + assert.Equal(t, uint64(0), rs.BlockNumber) } diff --git a/model/account/account.go b/model/account/account.go index e8a6f20..5c90510 100644 --- a/model/account/account.go +++ b/model/account/account.go @@ -403,7 +403,7 @@ type accountOptions struct { masterNode slip10.Node didMethod string rootIdentity *model.DID - firstBlock int64 + firstBlock uint64 entropyFunc EntropyFunction secondLevelRecoveryKey []byte log *zerolog.Logger @@ -476,7 +476,7 @@ func WithRootIdentity(rootIdentity *model.DID) Option { } } -func WithFirstBlock(firstBlock int64) Option { +func WithFirstBlock(firstBlock uint64) Option { return func(opts *accountOptions) error { opts.firstBlock = firstBlock return nil diff --git a/model/block.go b/model/block.go index f0a6e8b..a4d79d2 100644 --- a/model/block.go +++ b/model/block.go @@ -19,7 +19,7 @@ package model // Hash and ParentHash fields allow connecting a specific block // with the underlying block implementation. type Block struct { - Number int64 `json:"number"` + Number uint64 `json:"number"` Hash string `json:"hash"` ParentHash string `json:"parentHash,omitempty"` } diff --git a/model/dataset.go b/model/dataset.go index aca515b..84dfc8e 100644 --- a/model/dataset.go +++ b/model/dataset.go @@ -49,7 +49,7 @@ type DataSet interface { // Record returns the dataset's record structure Record() *Record // BlockNumber returns the number (ID) of the block where the dataset's record appeared. - BlockNumber() int64 + BlockNumber() uint64 // LockerID returns the ID of the locker that contains the dataset. LockerID() string // ParticipantID returns the ID (the corresponding identity's DID) of the locker participant diff --git a/model/dataset/dataset.go b/model/dataset/dataset.go index 37953a7..6fafbbd 100644 --- a/model/dataset/dataset.go +++ b/model/dataset/dataset.go @@ -29,7 +29,7 @@ type ( blobManager model.BlobManager record *model.Record lease *model.Lease - blockNumber int64 + blockNumber uint64 lockerID string participantID string accessToken string @@ -59,7 +59,7 @@ func WithLoadOptions(options LoadOptions) LoadOption { } } -func NewDataSetImpl(r *model.Record, lease *model.Lease, blockNumber int64, lockerID, participantID string, blobManager model.BlobManager) *DataSetImpl { +func NewDataSetImpl(r *model.Record, lease *model.Lease, blockNumber uint64, lockerID, participantID string, blobManager model.BlobManager) *DataSetImpl { return &DataSetImpl{ record: r, lease: lease, @@ -70,7 +70,7 @@ func NewDataSetImpl(r *model.Record, lease *model.Lease, blockNumber int64, lock } } -func NewRevokedDataSetImpl(r *model.Record, blockNumber int64, lockerID, participantID string) *DataSetImpl { +func NewRevokedDataSetImpl(r *model.Record, blockNumber uint64, lockerID, participantID string) *DataSetImpl { return &DataSetImpl{ record: r, blockNumber: blockNumber, @@ -186,7 +186,7 @@ func (d *DataSetImpl) Record() *model.Record { return d.record } -func (d *DataSetImpl) BlockNumber() int64 { +func (d *DataSetImpl) BlockNumber() uint64 { return d.blockNumber } diff --git a/model/dataset/future.go b/model/dataset/future.go index 62b3fae..d2f14be 100644 --- a/model/dataset/future.go +++ b/model/dataset/future.go @@ -69,7 +69,7 @@ func (f *recordFutureImpl) Wait(timeout time.Duration) error { return f.err } - var blockNumber int64 + var blockNumber uint64 blockNumber, f.err = WaitForConfirmation(f.ctx, f.ledger, f.ns, time.Second, timeout, f.waitList...) f.ready = true diff --git a/model/dataset/wait.go b/model/dataset/wait.go index 74437a0..bbb894a 100644 --- a/model/dataset/wait.go +++ b/model/dataset/wait.go @@ -25,7 +25,7 @@ import ( "github.com/rs/zerolog/log" ) -func WaitForConfirmation(ctx context.Context, ledger model.Ledger, ns notification.Service, interval, timeout time.Duration, recordID ...string) (int64, error) { +func WaitForConfirmation(ctx context.Context, ledger model.Ledger, ns notification.Service, interval, timeout time.Duration, recordID ...string) (uint64, error) { if len(recordID) == 0 { // fast exit if there's nothing to wait for @@ -41,7 +41,7 @@ func WaitForConfirmation(ctx context.Context, ledger model.Ledger, ns notificati // check if all the previous record got published if len(recordID) > 1 { - blockNumber = 0 + var blockNumber uint64 = 0 for _, rid := range recordID[0 : len(recordID)-1] { currentState, err := ledger.GetRecordState(ctx, rid) if err != nil { @@ -65,7 +65,7 @@ func WaitForConfirmation(ctx context.Context, ledger model.Ledger, ns notificati return blockNumber, nil } -func waitForOneRecord(ctx context.Context, ledger model.Ledger, ns notification.Service, interval, timeout time.Duration, recordID string) (int64, error) { +func waitForOneRecord(ctx context.Context, ledger model.Ledger, ns notification.Service, interval, timeout time.Duration, recordID string) (uint64, error) { var blockCh chan any diff --git a/model/ledger.go b/model/ledger.go index 11bfae0..95ec2e8 100644 --- a/model/ledger.go +++ b/model/ledger.go @@ -73,12 +73,12 @@ type ( // behind the record was revoked. GetRecordState(ctx context.Context, rid string) (*RecordState, error) // GetBlock returns a block definition for the given block number. - GetBlock(ctx context.Context, bn int64) (*Block, error) + GetBlock(ctx context.Context, bn uint64) (*Block, error) // GetBlockRecords returns a list of all ledger records included // in the block as an array of arrays of strings: // [record_id, routing_key, key_index]* // Returns ErrBlockNotFound error if block was not found. - GetBlockRecords(ctx context.Context, bn int64) ([][]string, error) + GetBlockRecords(ctx context.Context, bn uint64) ([][]string, error) // GetGenesisBlock returns the definition of the genesis block. // If there is no genesis block yet, it will return nil as a block. GetGenesisBlock(ctx context.Context) (*Block, error) @@ -87,7 +87,7 @@ type ( GetTopBlock(ctx context.Context) (*Block, error) // GetChain returns a sequence of block definitions of // the given length (depth), starting from the given block id - GetChain(ctx context.Context, startNumber int64, depth int) ([]*Block, error) + GetChain(ctx context.Context, startNumber uint64, depth int) ([]*Block, error) // GetDataAssetState returns the state of the given data asset. Returns // ErrDataAssetNotFound error if data asset not found. GetDataAssetState(ctx context.Context, id string) (DataAssetState, error) @@ -104,5 +104,5 @@ const ( type NewBlockMessage struct { Type string `json:"type"` - Number int64 `json:"number"` + Number uint64 `json:"number"` } diff --git a/model/locker.go b/model/locker.go index 7f9e557..daab25e 100644 --- a/model/locker.go +++ b/model/locker.go @@ -54,7 +54,7 @@ type ( RootPrivateKeyEnc string `json:"encryptedRootPrivateKey,omitempty"` // AcceptedAtBlock is the number of the block when the locker was accepted by the party // and registered in its root locker. - AcceptedAtBlock int64 `json:"acceptedAtBlock,omitempty"` + AcceptedAtBlock uint64 `json:"acceptedAtBlock,omitempty"` rootKeyPriv *hdkeychain.ExtendedKey rootKeyPub *hdkeychain.ExtendedKey @@ -82,14 +82,14 @@ type ( Sealed *time.Time `json:"sealed,omitempty"` // FirstBlock is the block number that was the height of the chain when the locker was created. // It is guaranteed that all records for this locker will be in blocks AFTER this block. - FirstBlock int64 `json:"firstBlock"` + FirstBlock uint64 `json:"firstBlock"` // LastBlock is the block number that was the height of the chain when the locker was sealed. // It is guaranteed that all records for this locker will be in blocks BEFORE this block. // NOT SUPPORTED. - LastBlock int64 `json:"lastBlock,omitempty"` + LastBlock uint64 `json:"lastBlock,omitempty"` // ThirdPartyAcceptedAtBlock is the number of the block when the locker was accepted by the owner // when the owner acts as a third party (is not a participant on the locker) - ThirdPartyAcceptedAtBlock int64 `json:"acceptedAtBlock,omitempty"` + ThirdPartyAcceptedAtBlock uint64 `json:"acceptedAtBlock,omitempty"` } ) @@ -257,7 +257,7 @@ func (l *Locker) GetParticipant(participantID string) *LockerParticipant { return nil } -func (l *Locker) AcceptedAtBlock() int64 { +func (l *Locker) AcceptedAtBlock() uint64 { us := l.Us() if us != nil && us.AcceptedAtBlock != 0 { return us.AcceptedAtBlock @@ -266,7 +266,7 @@ func (l *Locker) AcceptedAtBlock() int64 { } } -func (l *Locker) SetAcceptedAtBlock(block int64) { +func (l *Locker) SetAcceptedAtBlock(block uint64) { us := l.Us() if us != nil { us.AcceptedAtBlock = block @@ -522,7 +522,7 @@ func Them(did *DID, seed []byte) PartyOption { return withParty(did, seed, false) } -func GenerateLocker(accessLevel AccessLevel, name string, expires *time.Time, firstBlock int64, +func GenerateLocker(accessLevel AccessLevel, name string, expires *time.Time, firstBlock uint64, parties ...PartyOption) (*Locker, error) { participants := make([]*LockerParticipant, len(parties)) diff --git a/model/locker_test.go b/model/locker_test.go index e096e1f..bc6f1cc 100644 --- a/model/locker_test.go +++ b/model/locker_test.go @@ -84,7 +84,7 @@ func TestGenerateLocker(t *testing.T) { assert.Equal(t, "Test Locker", locker.Name) assert.Equal(t, AccessLevelHosted, locker.AccessLevel) assert.Equal(t, &expiryTime, locker.Expires) - assert.Equal(t, int64(123), locker.FirstBlock) + assert.Equal(t, uint64(123), locker.FirstBlock) assert.NotNil(t, locker.Participants) assert.Equal(t, 2, len(locker.Participants)) assert.Equal(t, did1.ID, locker.Participants[0].ID) diff --git a/model/raters/prv21/interface.go b/model/raters/prv21/interface.go index c2c1679..fb5dd3c 100644 --- a/model/raters/prv21/interface.go +++ b/model/raters/prv21/interface.go @@ -22,7 +22,7 @@ import ( ) const ( - NoBlockNumber int64 = -1 + NoBlockNumber uint64 = 0 ) type ( @@ -30,17 +30,17 @@ type ( // to algorithmically identify the "current" revision of the given variant, as well // as its current revision at a given block height. Rater interface { - AddRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64) (bool, error) + AddRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64) (bool, error) AddRevocation(ctx context.Context, rid string) error Head(ctx context.Context) string - HeadAt(ctx context.Context, blockID int64) string + HeadAt(ctx context.Context, blockID uint64) string } Revision interface { RecordID() string Status() model.RecordStatus - Block() int64 - EffectiveBlock() int64 + Block() uint64 + EffectiveBlock() uint64 Locker() string Participant() string AssetID() string @@ -48,20 +48,20 @@ type ( ImpressionID() string RevisionNumber() int64 CreatedAt() time.Time - HeadFrom() int64 - HeadTo() int64 + HeadFrom() uint64 + HeadTo() uint64 } RevisionStore interface { Head(ctx context.Context, variantID string) (Revision, error) - HeadAt(ctx context.Context, variantID string, blockNumber int64) (Revision, error) + HeadAt(ctx context.Context, variantID string, blockNumber uint64) (Revision, error) Revision(ctx context.Context, rid string) (Revision, error) - CreateRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64, - headFrom, headTo int64) error + CreateRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64, + headFrom, headTo uint64) error UpdateRevision(ctx context.Context, rid string, status model.RecordStatus, - headFrom, headTo int64) error + headFrom, headTo uint64) error RevokeRevision(ctx context.Context, rid string) error - SaveRevokedRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64) error + SaveRevokedRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64) error DataSet(ctx context.Context, rid string) (model.DataSet, error) } ) diff --git a/model/raters/prv21/rater.go b/model/raters/prv21/rater.go index ed8fd9f..2091844 100644 --- a/model/raters/prv21/rater.go +++ b/model/raters/prv21/rater.go @@ -38,12 +38,12 @@ func NewRater(variantID string, store RevisionStore) Rater { return r } -func (r *RaterImpl) AddRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64) (bool, error) { +func (r *RaterImpl) AddRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64) (bool, error) { imp := ds.Impression() varID := imp.GetVariantID() revNum := imp.Revision() - var headFrom int64 + var headFrom uint64 var currentHead Revision var err error @@ -129,7 +129,7 @@ func (r *RaterImpl) AddRevocation(ctx context.Context, rid string) error { return nil } -func (r *RaterImpl) HeadAt(ctx context.Context, blockID int64) string { +func (r *RaterImpl) HeadAt(ctx context.Context, blockID uint64) string { head, err := r.store.HeadAt(ctx, r.variantID, blockID) if err != nil { return "" diff --git a/model/raters/prv21/rater_test.go b/model/raters/prv21/rater_test.go index 1141d00..55c9fdf 100644 --- a/model/raters/prv21/rater_test.go +++ b/model/raters/prv21/rater_test.go @@ -30,8 +30,8 @@ type ( MockRevision struct { recordID string status model.RecordStatus - block int64 - effectiveBlock int64 + block uint64 + effectiveBlock uint64 locker string participant string assetID string @@ -39,8 +39,8 @@ type ( impressionID string revisionNumber int64 createdAt time.Time - headFrom int64 - headTo int64 + headFrom uint64 + headTo uint64 } MockRevisionStore struct { @@ -59,11 +59,11 @@ func (r *MockRevision) Status() model.RecordStatus { return r.status } -func (r *MockRevision) Block() int64 { +func (r *MockRevision) Block() uint64 { return r.block } -func (r *MockRevision) EffectiveBlock() int64 { +func (r *MockRevision) EffectiveBlock() uint64 { return r.effectiveBlock } @@ -95,15 +95,15 @@ func (r *MockRevision) CreatedAt() time.Time { return r.createdAt } -func (r *MockRevision) HeadFrom() int64 { +func (r *MockRevision) HeadFrom() uint64 { return r.headFrom } -func (r *MockRevision) HeadTo() int64 { +func (r *MockRevision) HeadTo() uint64 { return r.headTo } -func (r *MockRevision) Update(status model.RecordStatus, headFrom, headTo int64) { +func (r *MockRevision) Update(status model.RecordStatus, headFrom, headTo uint64) { r.status = status r.headFrom = headFrom r.headTo = headTo @@ -126,7 +126,7 @@ func (m *MockRevisionStore) Head(ctx context.Context, variantID string) (Revisio return nil, model.ErrRecordNotFound } -func (m *MockRevisionStore) HeadAt(ctx context.Context, variantID string, blockNumber int64) (Revision, error) { +func (m *MockRevisionStore) HeadAt(ctx context.Context, variantID string, blockNumber uint64) (Revision, error) { for _, rev := range m.revisions { if rev.VariantID() == variantID && rev.HeadFrom() <= blockNumber && @@ -145,7 +145,7 @@ func (m *MockRevisionStore) Revision(ctx context.Context, rid string) (Revision, return rev, nil } -func (m *MockRevisionStore) CreateRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64, headFrom, headTo int64) error { +func (m *MockRevisionStore) CreateRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64, headFrom, headTo uint64) error { imp := ds.Impression() varID := imp.GetVariantID() revNum := imp.Revision() @@ -170,7 +170,7 @@ func (m *MockRevisionStore) CreateRevision(ctx context.Context, ds model.DataSet return nil } -func (m *MockRevisionStore) UpdateRevision(ctx context.Context, rid string, status model.RecordStatus, headFrom, headTo int64) error { +func (m *MockRevisionStore) UpdateRevision(ctx context.Context, rid string, status model.RecordStatus, headFrom, headTo uint64) error { rev, found := m.revisions[rid] if !found { return model.ErrRecordNotFound @@ -192,7 +192,7 @@ func (m *MockRevisionStore) RevokeRevision(ctx context.Context, rid string) erro return nil } -func (m *MockRevisionStore) SaveRevokedRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber int64) error { +func (m *MockRevisionStore) SaveRevokedRevision(ctx context.Context, ds model.DataSet, effectiveBlockNumber uint64) error { panic("not implemented") } diff --git a/model/record.go b/model/record.go index df39fd3..16318b1 100644 --- a/model/record.go +++ b/model/record.go @@ -127,7 +127,7 @@ const ( type RecordState struct { Status RecordStatus `json:"status"` - BlockNumber int64 `json:"number"` + BlockNumber uint64 `json:"number"` } func (r *Record) Seal(pk *btcec.PrivateKey) error { diff --git a/model/scanner/scanner.go b/model/scanner/scanner.go index 32ffecc..ce59447 100644 --- a/model/scanner/scanner.go +++ b/model/scanner/scanner.go @@ -58,7 +58,7 @@ type ( } BlockNotification struct { - Block int64 `json:"b"` + Block uint64 `json:"b"` Datasets []DatasetNotification `json:"ds"` } @@ -68,7 +68,7 @@ type ( IndexID() string LockerConfigs() []*LockerConfig ConsumeBlock(ctx context.Context, n BlockNotification) error - NotifyScanCompleted(topBlock int64) error + NotifyScanCompleted(topBlock uint64) error InactiveSince() int64 Status() int SetStatus(status int) @@ -80,7 +80,7 @@ type ( LockerConfig struct { KeyID int `json:"key"` - LastBlock int64 `json:"last"` + LastBlock uint64 `json:"last"` PublicKeyStr string `json:"pubk"` Subscription Subscription `json:"-"` @@ -108,7 +108,7 @@ func NewScanner(ledgerAPI model.Ledger) *Scanner { } } -func (isu *Scanner) scanLedger(ctx context.Context, scannerList []*LockerConfig, startBlockNumber int64, endBlockNumber int64, blockBatchSize int) (int64, bool, error) { +func (isu *Scanner) scanLedger(ctx context.Context, scannerList []*LockerConfig, startBlockNumber uint64, endBlockNumber uint64, blockBatchSize int) (uint64, bool, error) { currentBlockNumber := startBlockNumber firstBlockIndex := 0 earlyExit := false @@ -117,7 +117,7 @@ AllBlocks: for { blocks, err := isu.ledgerAPI.GetChain(ctx, currentBlockNumber, blockBatchSize) if err != nil { - return -1, false, err + return 0, false, err } if len(blocks) == 0 { @@ -127,13 +127,13 @@ AllBlocks: for _, b := range blocks[firstBlockIndex:] { currentBlockNumber = b.Number - log.Debug().Int64("number", currentBlockNumber).Msg("Processing block") + log.Debug().Uint64("number", currentBlockNumber).Msg("Processing block") states := make(map[string]*subscriptionState) records, err := isu.ledgerAPI.GetBlockRecords(ctx, currentBlockNumber) if err != nil { - return -1, false, err + return 0, false, err } for _, v := range records { @@ -141,7 +141,7 @@ AllBlocks: routingKey := base58.Decode(v[1]) idx64, err := strconv.ParseUint(v[2], 10, 32) if err != nil { - return -1, false, err + return 0, false, err } idx := uint32(idx64) @@ -156,7 +156,7 @@ AllBlocks: k, err := cfg.publicKey.Derive(idx) if err != nil { - return -1, false, err + return 0, false, err } recordPubKey, _ := k.ECPubKey() indexKey := recordPubKey.SerializeCompressed() @@ -171,7 +171,7 @@ AllBlocks: // lazy-read record lr, err = isu.ledgerAPI.GetRecord(ctx, rid) if err != nil { - return -1, false, err + return 0, false, err } } @@ -313,8 +313,8 @@ func (isu *Scanner) scanOneRound(ctx context.Context) (bool, bool, error) { blockBatchSize := 10 complete := true - blockSeqNoList := make([]int64, 0) - blockToLockerConfigs := make(map[int64][]*LockerConfig) + blockSeqNoList := make([]uint64, 0) + blockToLockerConfigs := make(map[uint64][]*LockerConfig) for _, indexID := range isu.subscriptionList { sub := isu.subscriptions[indexID] if sub.Status() == ScanStatusActive { @@ -339,7 +339,7 @@ func (isu *Scanner) scanOneRound(ctx context.Context) (bool, bool, error) { return false, false, err } - var topBlockNumber int64 = -1 + var topBlockNumber uint64 exitedEarly := false for idx, blockNumber := range blockSeqNoList { @@ -350,7 +350,7 @@ func (isu *Scanner) scanOneRound(ctx context.Context) (bool, bool, error) { // pick the first block after the last scanned block startBlockNumber := blockNumber + 1 - endBlockNumber := int64(-1) + var endBlockNumber uint64 if idx < len(blockSeqNoList)-1 { endBlockNumber = blockSeqNoList[idx+1] } @@ -358,7 +358,7 @@ func (isu *Scanner) scanOneRound(ctx context.Context) (bool, bool, error) { accumulatedLockers = append(blockToLockerConfigs[blockNumber], accumulatedLockers...) if len(accumulatedLockers) == 0 { - log.Warn().Int("idx", idx).Int64("start", startBlockNumber).Int64("end", endBlockNumber). + log.Warn().Int("idx", idx).Uint64("start", startBlockNumber).Uint64("end", endBlockNumber). Msg("No lockers to sync in current round") continue } @@ -379,7 +379,7 @@ func (isu *Scanner) scanOneRound(ctx context.Context) (bool, bool, error) { accumulatedLockers = reducedScannerList } - log.Debug().Int("idx", idx).Int64("start", startBlockNumber).Int64("end", endBlockNumber). + log.Debug().Int("idx", idx).Uint64("start", startBlockNumber).Uint64("end", endBlockNumber). Int("lockerCount", len(accumulatedLockers)).Msg("Initiating new scanning round") topBlockNumber, exitedEarly, err = isu.scanLedger(ctx, accumulatedLockers, startBlockNumber, endBlockNumber, blockBatchSize) @@ -393,7 +393,7 @@ func (isu *Scanner) scanOneRound(ctx context.Context) (bool, bool, error) { restart := false - if topBlockNumber >= 0 { + if topBlockNumber > 0 { for _, indexID := range isu.subscriptionList { sub := isu.subscriptions[indexID] if err = sub.NotifyScanCompleted(topBlockNumber); err != nil { diff --git a/model/scanner/scanner_test.go b/model/scanner/scanner_test.go index 4b977a3..37e42ee 100644 --- a/model/scanner/scanner_test.go +++ b/model/scanner/scanner_test.go @@ -480,7 +480,7 @@ func (cc *CheckingConsumer) ConsumeBlock(ctx context.Context, indexID string, pa return nil } -func (cc *CheckingConsumer) NotifyScanCompleted(block int64) error { +func (cc *CheckingConsumer) NotifyScanCompleted(block uint64) error { return nil } diff --git a/model/scanner/subscription.go b/model/scanner/subscription.go index be5e9ac..6a9ad89 100644 --- a/model/scanner/subscription.go +++ b/model/scanner/subscription.go @@ -24,12 +24,12 @@ import ( ) type ( - PartyLookup func(keyID int) (string, string, string, int64) + PartyLookup func(keyID int) (string, string, string, uint64) IndexBlockConsumer interface { SetSubscription(sub Subscription) ConsumeBlock(ctx context.Context, indexID string, partyLookup PartyLookup, n BlockNotification) error - NotifyScanCompleted(block int64) error + NotifyScanCompleted(block uint64) error } IndexSubscription struct { @@ -48,12 +48,12 @@ type ( ParticipantID string SharedSecret string PublicKeyStr string - AcceptedAtBlock int64 + AcceptedAtBlock uint64 } LockerEntry struct { Locker *model.Locker - LastBlock int64 + LastBlock uint64 } ) @@ -92,7 +92,7 @@ func (w *IndexSubscription) ConsumeBlock(ctx context.Context, n BlockNotificatio return w.consumer.ConsumeBlock(ctx, w.indexID, w.partyLookup, n) } -func (w *IndexSubscription) partyLookup(keyID int) (string, string, string, int64) { +func (w *IndexSubscription) partyLookup(keyID int) (string, string, string, uint64) { p, found := w.keys[keyID] if !found { log.Warn().Int("KeyID", keyID).Msg("Invalid key ID") @@ -101,7 +101,7 @@ func (w *IndexSubscription) partyLookup(keyID int) (string, string, string, int6 return p.LockerID, p.ParticipantID, p.SharedSecret, p.AcceptedAtBlock } -func (w *IndexSubscription) NotifyScanCompleted(topBlock int64) error { +func (w *IndexSubscription) NotifyScanCompleted(topBlock uint64) error { return w.consumer.NotifyScanCompleted(topBlock) } diff --git a/node/api/ledger.go b/node/api/ledger.go index 822cdb0..53893ca 100644 --- a/node/api/ledger.go +++ b/node/api/ledger.go @@ -104,7 +104,7 @@ func (h *LedgerHandler) GetLedgerChainHandler(c *gin.Context) { return } - startBlockNumber, err := strconv.ParseInt(startBlockNumberStr, 10, 0) + startBlockNumber, err := strconv.ParseUint(startBlockNumberStr, 10, 0) if err != nil { log.Err(err).Str("number", startBlockNumberStr).Msg("Error when parsing start block number") _ = c.AbortWithError(http.StatusBadRequest, err) @@ -143,7 +143,7 @@ func (h *LedgerHandler) GetLedgerBlockHandler(c *gin.Context) { return } - blockNumber, err := strconv.ParseInt(blockNumberStr, 10, 0) + blockNumber, err := strconv.ParseUint(blockNumberStr, 10, 0) if err != nil { log.Err(err).Str("number", blockNumberStr).Msg("Error when parsing block number") _ = c.AbortWithError(http.StatusBadRequest, err) @@ -170,7 +170,7 @@ func (h *LedgerHandler) GetLedgerBlockRecordsHandler(c *gin.Context) { return } - blockNumber, err := strconv.ParseInt(blockNumberStr, 10, 0) + blockNumber, err := strconv.ParseUint(blockNumberStr, 10, 0) if err != nil { log.Err(err).Str("number", blockNumberStr).Msg("Error when parsing block number") _ = c.AbortWithError(http.StatusBadRequest, err) diff --git a/node/api/status.go b/node/api/status.go index 043fa5d..7d1b5a1 100644 --- a/node/api/status.go +++ b/node/api/status.go @@ -27,7 +27,7 @@ type ServerControls struct { MaintenanceMode bool `json:"maintenanceMode"` JWTPublicKey string `json:"jwtPublicKey"` GenesisBlockHash string `json:"genesis"` - TopBlock int64 `json:"top"` + TopBlock uint64 `json:"top"` } func GetStatusHandler(controls *ServerControls, ledger model.Ledger) func(c *gin.Context) { diff --git a/remote/caller/blocks.go b/remote/caller/blocks.go index eac87f0..c9394be 100644 --- a/remote/caller/blocks.go +++ b/remote/caller/blocks.go @@ -44,7 +44,7 @@ func (c *MetaLockerHTTPCaller) GetTopBlock(ctx context.Context) (*model.Block, e } } -func (c *MetaLockerHTTPCaller) GetBlock(ctx context.Context, bn int64) (*model.Block, error) { +func (c *MetaLockerHTTPCaller) GetBlock(ctx context.Context, bn uint64) (*model.Block, error) { var b model.Block err := c.client.LoadContents(ctx, http.MethodGet, fmt.Sprintf("/v1/ledger/block/%d", bn), nil, &b) if err != nil { @@ -54,7 +54,7 @@ func (c *MetaLockerHTTPCaller) GetBlock(ctx context.Context, bn int64) (*model.B } } -func (c *MetaLockerHTTPCaller) GetChain(ctx context.Context, startNumber int64, depth int) ([]*model.Block, error) { +func (c *MetaLockerHTTPCaller) GetChain(ctx context.Context, startNumber uint64, depth int) ([]*model.Block, error) { var blocks []*model.Block err := c.client.LoadContents(ctx, http.MethodGet, fmt.Sprintf("/v1/ledger/chain/%d/%d", startNumber, depth), nil, &blocks) if err != nil { @@ -64,7 +64,7 @@ func (c *MetaLockerHTTPCaller) GetChain(ctx context.Context, startNumber int64, } } -func (c *MetaLockerHTTPCaller) GetBlockRecords(ctx context.Context, bn int64) ([][]string, error) { +func (c *MetaLockerHTTPCaller) GetBlockRecords(ctx context.Context, bn uint64) ([][]string, error) { var recBytes []byte err := c.client.LoadContents(ctx, http.MethodGet, fmt.Sprintf("/v1/ledger/block/%d/records", bn), nil, &recBytes) if err != nil { diff --git a/remote/caller/types.go b/remote/caller/types.go index 15e42ab..63117e9 100644 --- a/remote/caller/types.go +++ b/remote/caller/types.go @@ -46,6 +46,6 @@ type ( MaintenanceMode bool `json:"maintenanceMode"` JWTPublicKey string `json:"jwtPublicKey"` GenesisBlockHash string `json:"genesis"` - TopBlock int64 `json:"top"` + TopBlock uint64 `json:"top"` } ) diff --git a/remote/factory.go b/remote/factory.go index bd9e40a..b106997 100644 --- a/remote/factory.go +++ b/remote/factory.go @@ -87,7 +87,7 @@ func NewWalletFactory(url string, indexClientSourceFn IndexClientSourceFn, accou return rf, nil } -func (rf *Factory) GetTopBlock() (int64, error) { +func (rf *Factory) GetTopBlock() (uint64, error) { controls, err := rf.httpCaller.GetServerControls(context.Background()) if err != nil { return 0, err diff --git a/sdk/testbase/dataset.go b/sdk/testbase/dataset.go index 65201ca..37c2655 100644 --- a/sdk/testbase/dataset.go +++ b/sdk/testbase/dataset.go @@ -30,7 +30,7 @@ import ( type MockDataSet struct { record *model.Record lease *model.Lease - blockNumber int64 + blockNumber uint64 lockerID string participantID string meta []byte @@ -38,7 +38,7 @@ type MockDataSet struct { var _ model.DataSet = (*MockDataSet)(nil) -func NewMockDataSet(r *model.Record, lease *model.Lease, blockNumber int64, lockerID, participantID string, meta any) *MockDataSet { +func NewMockDataSet(r *model.Record, lease *model.Lease, blockNumber uint64, lockerID, participantID string, meta any) *MockDataSet { ds := &MockDataSet{ record: r, lease: lease, @@ -106,7 +106,7 @@ func (d *MockDataSet) Record() *model.Record { return d.record } -func (d *MockDataSet) BlockNumber() int64 { +func (d *MockDataSet) BlockNumber() uint64 { return d.blockNumber } diff --git a/utils/type_conversion.go b/utils/type_conversion.go index a897c68..d71b535 100644 --- a/utils/type_conversion.go +++ b/utils/type_conversion.go @@ -50,6 +50,15 @@ func StringToInt64(v string) int64 { return i } +func Uint64ToString(v uint64) string { + return strconv.FormatUint(v, 10) +} + +func StringToUint64(v string) uint64 { + i, _ := strconv.ParseUint(v, 10, 0) + return i +} + func Float64ToString(v float64) string { return fmt.Sprintf("%1.15E", v) } diff --git a/wallet/datastore.go b/wallet/datastore.go index efebab6..ecc2ef1 100644 --- a/wallet/datastore.go +++ b/wallet/datastore.go @@ -84,7 +84,7 @@ func (c *localStoreImpl) getRootIndexRecord(ctx context.Context, id string) (*in return rec, nil } -type recordProcessor func(blockNumber int64, lockerID, participantID string, symKey *model.AESKey) error +type recordProcessor func(blockNumber uint64, lockerID, participantID string, symKey *model.AESKey) error func (c *localStoreImpl) processRecord(ctx context.Context, lr *model.Record, suggestedLockerID string, fn recordProcessor) error { rec, err := c.getRootIndexRecord(ctx, lr.ID) @@ -93,7 +93,7 @@ func (c *localStoreImpl) processRecord(ctx context.Context, lr *model.Record, su } var ( - blockNumber int64 + blockNumber uint64 lockerID string participantID string publicKey *btcec.PublicKey @@ -206,7 +206,7 @@ func (c *localStoreImpl) Load(ctx context.Context, recordID string, opts ...data } var ds model.DataSet - err = c.processRecord(ctx, rec, options.LockerID, func(blockNumber int64, lockerID, participantID string, symKey *model.AESKey) error { + err = c.processRecord(ctx, rec, options.LockerID, func(blockNumber uint64, lockerID, participantID string, symKey *model.AESKey) error { opRecBytes, err := c.offChainStorage.GetOperation(ctx, rec.OperationAddress) if err != nil { if rec.Status == model.StatusRevoked { @@ -593,7 +593,7 @@ func (c *localStoreImpl) AssetHead(ctx context.Context, headID string, opts ...d } var recordID string - err = c.processRecord(ctx, headRec, options.LockerID, func(blockNumber int64, lockerID, participantID string, symKey *model.AESKey) error { + err = c.processRecord(ctx, headRec, options.LockerID, func(blockNumber uint64, lockerID, participantID string, symKey *model.AESKey) error { headBodyBytes, err := base64.StdEncoding.DecodeString(headRec.HeadBody) if err != nil { return err diff --git a/wallet/index_updater.go b/wallet/index_updater.go index 1bc99d7..a41ee67 100644 --- a/wallet/index_updater.go +++ b/wallet/index_updater.go @@ -404,7 +404,7 @@ var ( managedLevels = []model.AccessLevel{model.AccessLevelManaged} ) -func (c *consumer) NotifyScanCompleted(topBlock int64) error { +func (c *consumer) NotifyScanCompleted(topBlock uint64) error { ctx := context.Background() if topBlock > 0 { diff --git a/wallet/wallet.go b/wallet/wallet.go index a9e9321..7972eac 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -547,7 +547,7 @@ func (dw *LocalDataWallet) getRootLockerID(level model.AccessLevel) string { } } -func (dw *LocalDataWallet) sendAccountUpdate(ctx context.Context, au *AccountUpdate, wait bool) (int64, error) { +func (dw *LocalDataWallet) sendAccountUpdate(ctx context.Context, au *AccountUpdate, wait bool) (uint64, error) { rootLockerID := dw.getRootLockerID(au.AccessLevel) lb, err := dw.DataStore().NewDataSetBuilder(ctx, rootLockerID, dataset.WithVault(dw.acct.DefaultVault)) From 5725bbb456ae7b21f36631307f877a6968fa4c2a Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 12 Jan 2025 01:22:35 +0000 Subject: [PATCH 2/9] A basic Flow contract to support MetaLocker ledger protocol --- go.mod | 210 +++- go.sum | 685 +++++++++++- ledger/local/ledger.go | 2 +- ledger/onflow/cachedb.go | 43 + ledger/onflow/common_test.go | 140 +++ ledger/onflow/contract_test.go | 399 +++++++ ledger/onflow/contracts/MetaLocker.cdc | 474 +++++++++ ledger/onflow/contracts/standard/Burner.cdc | 50 + .../onflow/contracts/standard/FlowToken.cdc | 297 ++++++ .../contracts/standard/FungibleToken.cdc | 240 +++++ .../standard/FungibleTokenMetadataViews.cdc | 185 ++++ .../standard/FungibleTokenSwitchboard.cdc | 376 +++++++ .../contracts/standard/MetadataViews.cdc | 805 ++++++++++++++ .../contracts/standard/NonFungibleToken.cdc | 293 ++++++ .../contracts/standard/ViewResolver.cdc | 58 ++ ledger/onflow/embedded.go | 22 + ledger/onflow/embedded_test.go | 22 + ledger/onflow/emulator/inmemory.go | 83 ++ ledger/onflow/flow.json | 128 +++ ledger/onflow/ledger.go | 985 ++++++++++++++++++ ledger/onflow/ledger_test.go | 403 +++++++ ledger/onflow/templates.go | 22 + .../scripts/account_balance_flow.cdc | 13 + .../scripts/metalocker_get_asset_head.cdc | 7 + .../scripts/metalocker_get_block.cdc | 7 + .../metalocker_get_data_asset_counter.cdc | 7 + .../metalocker_get_genesis_block_height.cdc | 7 + .../scripts/metalocker_get_record.cdc | 7 + .../metalocker_get_top_block_height.cdc | 7 + .../metalocker_get_top_block_number.cdc | 7 + .../transactions/account_fund_flow.cdc | 37 + .../transactions/metalocker_submit_record.cdc | 9 + ledger/onflow/templates_test.go | 74 ++ ledger/onflow/types.go | 268 +++++ ledger/onflow/types_test.go | 50 + model/block.go | 16 +- model/dataset/wait.go | 3 +- model/head.go | 4 +- model/head_test.go | 2 +- model/locker.go | 4 +- model/locker_test.go | 2 +- model/record.go | 154 +++ model/scanner/scanner_test.go | 6 +- node/api/access_key_test.go | 2 +- node/api/accounts_test.go | 2 +- node/api/identity_test.go | 2 +- node/api/locker_test.go | 2 +- node/api/property_test.go | 2 +- sdk/testbase/environment.go | 51 +- utils/bolt.go | 16 + wallet/datastore.go | 140 +-- wallet/locker.go | 2 +- 52 files changed, 6634 insertions(+), 198 deletions(-) create mode 100644 ledger/onflow/cachedb.go create mode 100644 ledger/onflow/common_test.go create mode 100644 ledger/onflow/contract_test.go create mode 100644 ledger/onflow/contracts/MetaLocker.cdc create mode 100644 ledger/onflow/contracts/standard/Burner.cdc create mode 100644 ledger/onflow/contracts/standard/FlowToken.cdc create mode 100644 ledger/onflow/contracts/standard/FungibleToken.cdc create mode 100644 ledger/onflow/contracts/standard/FungibleTokenMetadataViews.cdc create mode 100644 ledger/onflow/contracts/standard/FungibleTokenSwitchboard.cdc create mode 100644 ledger/onflow/contracts/standard/MetadataViews.cdc create mode 100644 ledger/onflow/contracts/standard/NonFungibleToken.cdc create mode 100644 ledger/onflow/contracts/standard/ViewResolver.cdc create mode 100644 ledger/onflow/embedded.go create mode 100644 ledger/onflow/embedded_test.go create mode 100644 ledger/onflow/emulator/inmemory.go create mode 100644 ledger/onflow/flow.json create mode 100644 ledger/onflow/ledger.go create mode 100644 ledger/onflow/ledger_test.go create mode 100644 ledger/onflow/templates.go create mode 100644 ledger/onflow/templates/scripts/account_balance_flow.cdc create mode 100644 ledger/onflow/templates/scripts/metalocker_get_asset_head.cdc create mode 100644 ledger/onflow/templates/scripts/metalocker_get_block.cdc create mode 100644 ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc create mode 100644 ledger/onflow/templates/scripts/metalocker_get_genesis_block_height.cdc create mode 100644 ledger/onflow/templates/scripts/metalocker_get_record.cdc create mode 100644 ledger/onflow/templates/scripts/metalocker_get_top_block_height.cdc create mode 100644 ledger/onflow/templates/scripts/metalocker_get_top_block_number.cdc create mode 100644 ledger/onflow/templates/transactions/account_fund_flow.cdc create mode 100644 ledger/onflow/templates/transactions/metalocker_submit_record.cdc create mode 100644 ledger/onflow/templates_test.go create mode 100644 ledger/onflow/types.go create mode 100644 ledger/onflow/types_test.go diff --git a/go.mod b/go.mod index 305d737..c9057a4 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.23.3 require ( entgo.io/ent v0.11.6 + github.com/alitto/pond/v2 v2.1.6 github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.4 @@ -15,6 +16,7 @@ require ( github.com/cskr/pubsub v1.0.2 github.com/fergusstrange/embedded-postgres v1.29.0 github.com/gabriel-vasile/mimetype v1.4.7 + github.com/gammazero/deque v1.0.0 github.com/gin-contrib/cors v1.7.2 github.com/gin-gonic/gin v1.10.0 github.com/gobuffalo/packr/v2 v2.8.3 @@ -30,8 +32,12 @@ require ( github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 github.com/multiformats/go-multihash v0.2.3 github.com/olekukonko/tablewriter v0.0.5 + github.com/onflow/cadence v1.2.1 + github.com/onflow/flow-go-sdk v1.2.2 + github.com/onflow/flowkit/v2 v2.1.0 github.com/piprate/json-gold v0.5.0 github.com/piprate/restgate v0.0.0-20190903092639-61855bc1bc5f + github.com/piprate/splash v0.0.0-20250112112525-5f9e7f5f169a github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 github.com/tyler-smith/go-bip39 v1.1.0 @@ -44,91 +50,273 @@ require ( require ( ariga.io/atlas v0.9.1-0.20230119123307-a3ab6808892b // indirect + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/kms v1.15.7 // indirect + cloud.google.com/go/storage v1.38.0 // indirect + github.com/DataDog/zstd v1.5.2 // indirect + github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.2 // indirect github.com/agext/levenshtein v1.2.1 // indirect github.com/aphistic/sweet v0.3.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.1 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/coreos/go-semver v0.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/frankban/quicktest v1.14.3 // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.0 // indirect + github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ef-ds/deque v1.0.4 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-ethereum v1.13.10 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c // indirect + github.com/fxamacker/circlehash v0.3.0 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/glebarez/go-sqlite v1.22.0 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/inflect v0.19.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/gobuffalo/logger v1.0.6 // indirect github.com/gobuffalo/packd v1.0.1 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.2.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.2 // indirect + github.com/gosuri/uilive v0.0.4 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.2.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl/v2 v2.13.0 // indirect github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/holiman/uint256 v1.3.0 // indirect + github.com/huandu/go-clone v1.6.0 // indirect + github.com/huandu/go-clone/generic v1.7.2 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/invopop/jsonschema v0.7.0 // indirect + github.com/ipfs/bbloom v0.0.4 // indirect + github.com/ipfs/boxo v0.17.1-0.20240131173518-89bceff34bf1 // indirect + github.com/ipfs/go-block-format v0.2.0 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-datastore v0.6.0 // indirect + github.com/ipfs/go-ipfs-util v0.0.3 // indirect + github.com/ipfs/go-ipld-format v0.6.0 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/jbenet/goprocess v0.1.4 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/k0kubun/pp v3.0.1+incompatible // indirect github.com/karrick/godirwalk v1.16.1 // indirect + github.com/kevinburke/go-bindata v3.24.0+incompatible // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-libp2p v0.32.2 // indirect + github.com/libp2p/go-libp2p-pubsub v0.10.0 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/lmars/go-slip10 v0.0.0-20190606092855-400ba44fee12 // indirect + github.com/logrusorgru/aurora v2.0.3+incompatible // indirect + github.com/logrusorgru/aurora/v4 v4.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/markbates/errx v1.1.0 // indirect github.com/markbates/oncer v1.0.0 // indirect github.com/markbates/safe v1.0.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/multiformats/go-varint v0.0.6 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.12.2 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/onflow/atree v0.8.0 // indirect + github.com/onflow/crypto v0.25.2 // indirect + github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 // indirect + github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 // indirect + github.com/onflow/flow-emulator v1.1.0 // indirect + github.com/onflow/flow-ft/lib/go/contracts v1.0.1 // indirect + github.com/onflow/flow-ft/lib/go/templates v1.0.1 // indirect + github.com/onflow/flow-go v0.38.0-preview.0.0.20241022154145-6a254edbec23 // indirect + github.com/onflow/flow-nft/lib/go/contracts v1.2.2 // indirect + github.com/onflow/flow-nft/lib/go/templates v1.2.1 // indirect + github.com/onflow/flow/protobuf/go/flow v0.4.7 // indirect + github.com/onflow/go-ethereum v1.14.7 // indirect + github.com/onflow/nft-storefront/lib/go/contracts v1.0.0 // indirect + github.com/onflow/sdks v0.6.0-preview.1 // indirect + github.com/onflow/wal v1.0.2 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pjebs/jsonerror v0.0.0-20190614034432-63ef9a8df848 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/psiemens/graceland v1.0.0 // indirect + github.com/psiemens/sconfig v0.1.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect + github.com/sethvargo/go-retry v0.2.3 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/slok/go-http-metrics v0.10.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/afero v1.10.0 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.15.0 // indirect + github.com/status-im/keycard-go v0.2.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/unrolled/render v1.0.1 // indirect + github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect + github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect + github.com/vmihailenco/tagparser v0.1.1 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/zclconf/go-cty v1.8.0 // indirect - go.uber.org/atomic v1.7.0 // indirect + github.com/zeebo/blake3 v0.2.3 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.8.0 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.31.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.9.0 // indirect golang.org/x/sys v0.27.0 // indirect golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.5.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + gonum.org/v1/gonum v0.14.0 // indirect + google.golang.org/api v0.169.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect + google.golang.org/grpc v1.64.1 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.1.6 // indirect + lukechampine.com/blake3 v1.3.0 // indirect + modernc.org/libc v1.37.6 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/sqlite v1.28.0 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index e094ccb..78c7d59 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -17,18 +18,27 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM= +cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -38,6 +48,9 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= entgo.io/ent v0.11.6 h1:fMQwhuzbPv12AXdrAGyHoOcgh9r0D9F8WEsCRoUWxVc= entgo.io/ent v0.11.6/go.mod h1:d4yUWiwY3NQtjGvINzAhUyypopfeEKOxcxLN7D5yM7o= @@ -48,8 +61,23 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc h1:DCHzPQOcU/7gwDTWbFQZc5qHMPS1g0xTO56k8NXsv9M= +github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc/go.mod h1:LJM5a3zcIJ/8TmZwlUczvROEJT8ntOdhdG9jjcR1B0I= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -58,6 +86,10 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alitto/pond/v2 v2.1.6 h1:6U3nSOjxpuNyvjIKjjRkpS2JDdgX5JqBm9GO2urcCjM= +github.com/alitto/pond/v2 v2.1.6/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a h1:2KLQMJ8msqoPHIPDufkxVcoTtcmE5+1sL9950m4R9Pk= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= @@ -65,26 +97,59 @@ github.com/aphistic/sweet v0.3.0 h1:xZTMfCoMsjWubPNxOBODluBC4qfGP0CdRJ88jon46XE= github.com/aphistic/sweet v0.3.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= +github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo= +github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 h1:tcFliCWne+zOuUfKNRn8JdFBuWPDuISDH08wD2ULkhk= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8/go.mod h1:JTnlBSot91steJeti4ryyu/tLd4Sk84O5W22L7O2EQU= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33 h1:fAoVmNGhir6BR+RU0/EI+6+D7abM+MCwWf8v4ip5jNI= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33/go.mod h1:84XgODVR8uRhmOnUkKGUZKqIMxmjmLOR8Uyp7G/TPwc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 h1:ZSIPAkAsCCjYrhqfw2+lNzWDzxzHXEckFkTePL5RSWQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14/go.mod h1:AyGgqiKv9ECM6IZeNQtdT8NnMvUb3/2wokeq2Fgryto= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 h1:BBYoNQt2kUZUUK4bIPsKrCcjVPUMNsgQpNAwhznK/zo= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18/go.mod h1:NS55eQ4YixUJPTC+INxi2/jCqe1y2Uw3rnh9wEOVJxY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 h1:HfVVR1vItaG6le+Bpw6P4midjBDMKnjMyZnw9MXYUcE= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17/go.mod h1:YqMdV+gEKCQ59NrB7rzrJdALeBIsYiVi8Inj3+KcqHI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 h1:3/gm/JTX9bX8CpzTgIlrtYpB3EVBDxyg/GY/QdcIEZw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11/go.mod h1:fmgDANqTUCxciViKl9hb/zD5LFbvPINFRgWhDbR+vZo= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= @@ -118,8 +183,17 @@ github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKz github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -132,15 +206,52 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= +github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= @@ -148,11 +259,28 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dhui/dktest v0.4.3 h1:wquqUxAFdcUgabAVLvSCOKOlag5cIZuaOjYIBOWdsR0= github.com/dhui/dktest v0.4.3/go.mod h1:zNK8IwktWzQRm6I/l2Wjp7MakiyaFWv4G1hjmodmMTs= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -164,8 +292,14 @@ github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI= +github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= +github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -173,23 +307,45 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.10 h1:Ppdil79nN+Vc+mXfge0AuUgmKWuVv4eMqzoIVSdqZek= +github.com/ethereum/go-ethereum v1.13.10/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fergusstrange/embedded-postgres v1.29.0 h1:Uv8hdhoiaNMuH0w8UuGXDHr60VoAQPFdgx7Qf3bzXJM= github.com/fergusstrange/embedded-postgres v1.29.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/flynn/noise v1.0.1 h1:vPp/jdQLXC6ppsXSj/pM3W1BIJ5FEHE2TulSJBpb43Y= +github.com/flynn/noise v1.0.1/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 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/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= +github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34= +github.com/gammazero/deque v1.0.0/go.mod h1:iflpYvtGfM3U8S8j+sZEKIak3SAKYpA5/SQewgfXDKo= +github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= +github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw= github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E= @@ -197,22 +353,36 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= +github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -223,7 +393,11 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= @@ -236,8 +410,14 @@ github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XE github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= @@ -245,9 +425,14 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y= github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -275,9 +460,13 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -296,9 +485,16 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -308,20 +504,42 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= +github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA= +github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= +github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -350,8 +568,8 @@ github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= -github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= @@ -367,6 +585,10 @@ github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc= @@ -388,10 +610,52 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.0 h1:4wdcm/tnd0xXdu7iS3ruNvxkWwrb4aeBQv19ayYn8F4= +github.com/holiman/uint256 v1.3.0/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/go-clone v1.6.0 h1:HMo5uvg4wgfiy5FoGOqlFLQED/VGRm2D9Pi8g1FXPGc= +github.com/huandu/go-clone v1.6.0/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5TbTVaXE= +github.com/huandu/go-clone/generic v1.7.2 h1:47pQphxs1Xc9cVADjOHN+Bm5D0hNagwH9UXErbxgVKA= +github.com/huandu/go-clone/generic v1.7.2/go.mod h1:xgd9ZebcMsBWWcBx5mVMCoqMX24gLWr5lQicr+nVXNs= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= +github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So= +github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= +github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= +github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= +github.com/ipfs/boxo v0.17.1-0.20240131173518-89bceff34bf1 h1:5H/HYvdmbxp09+sAvdqJzyrWoyCS6OroeW9Ym06Tb+0= +github.com/ipfs/boxo v0.17.1-0.20240131173518-89bceff34bf1/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= +github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= +github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= +github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= +github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= +github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U= +github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg= +github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= +github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= +github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= +github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= +github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= +github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -400,15 +664,24 @@ github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jamesruan/sodium v0.0.0-20181216154042-9620b83ffeae h1:FFafUVNizQIaIHAqmDKXQlZ+8/LplHZbL11emgcto+M= github.com/jamesruan/sodium v0.0.0-20181216154042-9620b83ffeae/go.mod h1:GZ0frHfchUw9cjP3CK1J7+M74KNo7zrhffQxTeuf5I8= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= +github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= 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= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -421,13 +694,23 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= +github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kevinburke/go-bindata v3.24.0+incompatible h1:qajFA3D0pH94OTLU4zcCCKCDgR+Zr2cZK/RPJHDdFoY= +github.com/kevinburke/go-bindata v3.24.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= @@ -435,34 +718,82 @@ github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYg github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= 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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= +github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= +github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= +github.com/libp2p/go-libp2p v0.32.2 h1:s8GYN4YJzgUoyeYNPdW7JZeZ5Ee31iNaIBfGYMAY4FQ= +github.com/libp2p/go-libp2p v0.32.2/go.mod h1:E0LKe+diV/ZVJVnOJby8VC5xzHF0660osg71skcxJvk= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-kad-dht v0.25.2 h1:FOIk9gHoe4YRWXTu8SY9Z1d0RILol0TrtApsMDPjAVQ= +github.com/libp2p/go-libp2p-kad-dht v0.25.2/go.mod h1:6za56ncRHYXX4Nc2vn8z7CZK0P4QiMcrn77acKLM2Oo= +github.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9GiSErbQQo0= +github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0= +github.com/libp2p/go-libp2p-pubsub v0.10.0 h1:wS0S5FlISavMaAbxyQn3dxMOe2eegMfswM471RuHJwA= +github.com/libp2p/go-libp2p-pubsub v0.10.0/go.mod h1:1OxbaT/pFRO5h+Dpze8hdHQ63R0ke55XTs6b6NwLLkw= +github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= +github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= +github.com/libp2p/go-libp2p-routing-helpers v0.7.3 h1:u1LGzAMVRK9Nqq5aYDVOiq/HaB93U9WWczBzGyAC5ZY= +github.com/libp2p/go-libp2p-routing-helpers v0.7.3/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= +github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= +github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= +github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= +github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= +github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/lmars/go-slip10 v0.0.0-20190606092855-400ba44fee12 h1:qFV7dBLhw5z4hka5gjtIzg1Kq9ie8t8P7Cy0uIxRyAQ= +github.com/lmars/go-slip10 v0.0.0-20190606092855-400ba44fee12/go.mod h1:QIsK6U93yCP6TnGsShCv5wl4gcz/mpCHl+aToBsl5Sc= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= +github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -476,20 +807,32 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +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/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -511,6 +854,9 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -528,34 +874,100 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 h1:31Y+Yu373ymebRdJN1cWLLooHH8xAr0MhKTEJGV/87g= github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021/go.mod h1:WERUkUryfUWlrHnFSO/BEUZ+7Ns8aZy7iVOGewxKzcc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.12.2 h1:9G9sTY/wCYajKa9lyfWPmpZAwe6oV+Wb1zcmMS1HG24= +github.com/multiformats/go-multiaddr v0.12.2/go.mod h1:GKyaTYjZRdcUhyOetrxTk9z0cW+jA/YrnqTOvKgi44M= +github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= +github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= +github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onflow/atree v0.8.0 h1:qg5c6J1gVDNObughpEeWm8oxqhPGdEyGrda121GM4u0= +github.com/onflow/atree v0.8.0/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= +github.com/onflow/cadence v1.2.1 h1:hmSsgX3rTsp2E5qTSl1JXINt8qepdRrHTwDSYqN5Nxs= +github.com/onflow/cadence v1.2.1/go.mod h1:fJxxOAp1wnWDfOHT8GOc1ypsU0RR5E3z51AhG8Yf5jg= +github.com/onflow/crypto v0.25.2 h1:GjHunqVt+vPcdqhxxhAXiMIF3YiLX7gTuTR5O+VG2ns= +github.com/onflow/crypto v0.25.2/go.mod h1:fY7eLqUdMKV8EGOw301unP8h7PvLVy8/6gVR++/g0BY= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 h1:R86HaOuk6vpuECZnriEUE7bw9inC2AtdSn8lL/iwQLQ= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0/go.mod h1:9asTBnB6Tw2UlVVtQKyS/egYv3xr4zVlJnJ75z1dfac= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 h1:u2DAG8pk0xFH7TwS70t1gSZ/FtIIZWMSNyiu4SeXBYg= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0/go.mod h1:pN768Al/wLRlf3bwugv9TyxniqJxMu4sxnX9eQJam64= +github.com/onflow/flow-emulator v1.1.0 h1:ocF5Pv/zrY5jb0iMxQ15NOM6EuS9Y/N+SUzREi+lKcg= +github.com/onflow/flow-emulator v1.1.0/go.mod h1:wuSY3boFgO+ZRW8PGeWcs+iwcicqkO58G1iPHqRQYDY= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1 h1:Ts5ob+CoCY2EjEd0W6vdLJ7hLL3SsEftzXG2JlmSe24= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= +github.com/onflow/flow-ft/lib/go/templates v1.0.1 h1:FDYKAiGowABtoMNusLuRCILIZDtVqJ/5tYI4VkF5zfM= +github.com/onflow/flow-ft/lib/go/templates v1.0.1/go.mod h1:uQ8XFqmMK2jxyBSVrmyuwdWjTEb+6zGjRYotfDJ5pAE= +github.com/onflow/flow-go v0.38.0-preview.0.0.20241022154145-6a254edbec23 h1:spF44tXZ341oVDTuXzzKTQ0W6rwZFV9r2/SRVVaMReo= +github.com/onflow/flow-go v0.38.0-preview.0.0.20241022154145-6a254edbec23/go.mod h1:6f7CTcguVOBINmraaWMiij5e2zu7W2mKsOmXAfvCZ2g= +github.com/onflow/flow-go-sdk v1.2.2 h1:F78Sq/VaExgtaQv739k06gnx2aIyLF5wVE0XwxFpmsc= +github.com/onflow/flow-go-sdk v1.2.2/go.mod h1:yhQ5+Sp2xWoCQ1fuRDswawTDQ0ng0z5nTkFVH82xL7E= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2 h1:XFERNVUDGbZ4ViZjt7P1cGD80mO1PzUJYPfdhXFsGbQ= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2/go.mod h1:eZ9VMMNfCq0ho6kV25xJn1kXeCfxnkhj3MwF3ed08gY= +github.com/onflow/flow-nft/lib/go/templates v1.2.1 h1:SAALMZPDw9Eb9p5kSLnmnFxjyig1MLiT4JUlLp0/bSE= +github.com/onflow/flow-nft/lib/go/templates v1.2.1/go.mod h1:W6hOWU0xltPqNpv9gQX8Pj8Jtf0OmRxc1XX2V0kzJaI= +github.com/onflow/flow/protobuf/go/flow v0.4.7 h1:iP6DFx4wZ3ETORsyeqzHu7neFT3d1CXF6wdK+AOOjmc= +github.com/onflow/flow/protobuf/go/flow v0.4.7/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= +github.com/onflow/flowkit/v2 v2.1.0 h1:HWx3wLMOZltC8D12YZn11bYDUaLBeVq3qVy2TY+5b7k= +github.com/onflow/flowkit/v2 v2.1.0/go.mod h1:LgV2PJRp9zRtmThruzJ/uaAxPgplAGEA+gBzLODm6Q4= +github.com/onflow/go-ethereum v1.14.7 h1:gg3awYqI02e3AypRdpJKEvNTJ6kz/OhAqRti0h54Wlc= +github.com/onflow/go-ethereum v1.14.7/go.mod h1:zV14QLrXyYu5ucvcwHUA0r6UaqveqbXaehAVQJlSW+I= +github.com/onflow/nft-storefront/lib/go/contracts v1.0.0 h1:sxyWLqGm/p4EKT6DUlQESDG1ZNMN9GjPCm1gTq7NGfc= +github.com/onflow/nft-storefront/lib/go/contracts v1.0.0/go.mod h1:kMeq9zUwCrgrSojEbTUTTJpZ4WwacVm2pA7LVFr+glk= +github.com/onflow/sdks v0.6.0-preview.1 h1:mb/cUezuqWEP1gFZNAgUI4boBltudv4nlfxke1KBp9k= +github.com/onflow/sdks v0.6.0-preview.1/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= +github.com/onflow/wal v1.0.2 h1:5bgsJVf2O3cfMNK12fiiTyYZ8cOrUiELt3heBJfHOhc= +github.com/onflow/wal v1.0.2/go.mod h1:iMC8gkLqu4nkbkAla5HkSBb+FGyQOZiWz3DYm2wSXCk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= +github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= +github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -565,10 +977,14 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/piprate/json-gold v0.5.0 h1:RmGh1PYboCFcchVFuh2pbSWAZy4XJaqTMU4KQYsApbM= github.com/piprate/json-gold v0.5.0/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= github.com/piprate/restgate v0.0.0-20190903092639-61855bc1bc5f h1:hviG4TlBNxG6FlseJEFuAOtWupUuXk5XXKBb2OrW0Pw= github.com/piprate/restgate v0.0.0-20190903092639-61855bc1bc5f/go.mod h1:HXYlY4epePEyfl2jXu+OoNfD6oA4PG135D2GDSwGR5A= +github.com/piprate/splash v0.0.0-20250112112525-5f9e7f5f169a h1:jVUcECjqrhboBa0ZuJAuPhH3Ncb37byQuxJWKos3D8s= +github.com/piprate/splash v0.0.0-20250112112525-5f9e7f5f169a/go.mod h1:krpaER4ul3b21ixGIKqByCdKOR7z5bbdevkDWO6zyVw= github.com/pjebs/jsonerror v0.0.0-20190614034432-63ef9a8df848 h1:XowRe4lNFAvUyqrGWsw5iXgSIAB88dvwkhG6Y358Mpk= github.com/pjebs/jsonerror v0.0.0-20190614034432-63ef9a8df848/go.mod h1:aTxn8DgzMXFgHW45SD0fKjhF3+vSL5Adh0+vhWmVH4g= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -577,40 +993,78 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/psiemens/graceland v1.0.0 h1:L580AVV4Q2XLcPpmvxJRH9UpEAYr/eu2jBKmMglhvM8= +github.com/psiemens/graceland v1.0.0/go.mod h1:1Tof+vt1LbmcZFE0lzgdwMN0QBymAChG3FRgDx8XisU= +github.com/psiemens/sconfig v0.1.0 h1:xfWqW+TRpih7mXZIqKYTmpRhlZLQ1kbxV8EjllPv76s= +github.com/psiemens/sconfig v0.1.0/go.mod h1:+MLKqdledP/8G3rOBpknbLh0IclCf4WneJUtS26JB2U= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q= +github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= +github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= +github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= +github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= +github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -618,10 +1072,17 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= +github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= +github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sethvargo/go-retry v0.2.3 h1:oYlgvIvsju3jNbottWABtbnoLC+GDtLdBHxKWxQm/iU= +github.com/sethvargo/go-retry v0.2.3/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -629,24 +1090,49 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/slok/go-http-metrics v0.10.0 h1:rh0LaYEKza5eaYRGDXujKrOln57nHBi4TtVhmNEpbgM= +github.com/slok/go-http-metrics v0.10.0/go.mod h1:lFqdaS4kWMfUKCSukjC47PdCeTk+hXDUVm8kLHRqJ38= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v0.0.0-20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -659,24 +1145,52 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg= +github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo= +github.com/thoas/go-funk v0.9.2 h1:oKlNYv0AY5nyf9g+/GhMgS/UO2ces0QRdPKwkhY3VCk= +github.com/thoas/go-funk v0.9.2/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d h1:5JInRQbk5UBX8JfUvKh2oYTLMVwj3p6n+wapDDm7hko= +github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d/go.mod h1:Nlx5Y115XQvNcIdIy7dZXaNSUpzwBSge4/Ivk93/Yog= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/unrolled/render v1.0.1 h1:VDDnQQVfBMsOsp3VaCJszSO0nkBIVEYoPWeRThk9spY= github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -685,8 +1199,17 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA= github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= +github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -702,25 +1225,55 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -729,7 +1282,11 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -742,6 +1299,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -767,6 +1326,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -776,12 +1336,14 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -809,12 +1371,15 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -829,6 +1394,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -840,6 +1407,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -847,7 +1415,9 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -859,8 +1429,10 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -886,6 +1458,7 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -898,6 +1471,7 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -905,17 +1479,24 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -931,6 +1512,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -939,6 +1522,7 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -989,14 +1573,23 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1019,6 +1612,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY= +google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1026,6 +1621,8 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1062,15 +1659,24 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No= +google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -1090,6 +1696,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1114,11 +1722,16 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1132,6 +1745,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1139,10 +1754,24 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c= -lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw= +modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/ledger/local/ledger.go b/ledger/local/ledger.go index 574eab6..c63500c 100644 --- a/ledger/local/ledger.go +++ b/ledger/local/ledger.go @@ -646,7 +646,7 @@ func (bl *BoltLedger) SubmitNewBlock(block *model.Block, records []*model.Record } case model.OpTypeLeaseRevocation: rb := tx.Bucket([]byte(RecordsKey)) - if blockCompBucket == nil { + if rb == nil { return fmt.Errorf("bucket %s not found", RecordsKey) } diff --git a/ledger/onflow/cachedb.go b/ledger/onflow/cachedb.go new file mode 100644 index 0000000..ad2da24 --- /dev/null +++ b/ledger/onflow/cachedb.go @@ -0,0 +1,43 @@ +package onflow + +import ( + "github.com/piprate/metalocker/utils" + "go.etcd.io/bbolt" +) + +const ( + + // root buckets + + BlocksKey = "blocks" + BlockRecordsKey = "block_records" + RecordsKey = "records" + ControlsKey = "controls" + + // control variables + + GenesisBlockHeightKey = "genesis_block_height" + TopFlowBlockHeightKey = "top_flow_block_height" + TopBlockNumberKey = "top_block_number" +) + +var ( + buckets = []string{BlocksKey, BlockRecordsKey, RecordsKey, ControlsKey} +) + +func InstallLedgerSchema(bc *utils.BoltClient) error { + err := bc.DB.Update(func(tx *bbolt.Tx) error { + for _, bucket := range buckets { + _, err := tx.CreateBucketIfNotExists([]byte(bucket)) + if err != nil { + return err + } + } + return nil + }) + if err != nil { + return err + } + + return nil +} diff --git a/ledger/onflow/common_test.go b/ledger/onflow/common_test.go new file mode 100644 index 0000000..c229247 --- /dev/null +++ b/ledger/onflow/common_test.go @@ -0,0 +1,140 @@ +package onflow_test + +import ( + "context" + "strings" + "testing" + + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/onflow/cadence" + "github.com/onflow/flow-go-sdk" + . "github.com/piprate/metalocker/ledger/onflow" + "github.com/piprate/metalocker/model" + "github.com/piprate/metalocker/utils/jsonw" + "github.com/piprate/splash" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + adminAccountName = "emulator-metalocker-admin" + platformAccountName = "emulator-metalocker-platform" + user1AccountName = "emulator-user1" + user2AccountName = "emulator-user2" + user3AccountName = "emulator-user3" + + sampleLease1 = `{ + "datasetType": "graph", + "expire": "1970-01-01T00:16:40Z", + "id": "Gm8tPkhqFzDoXNLTaCEfcHxdSKUmaWwRnQfy8V38NUab", + "impression": { + "@context": "https://piprate.org/context/piprate.jsonld", + "asset": "did:piprate:FdywEGiR5nubMFZgbfneUdAx5V858eaiRcBGRGVpj3Y6", + "generatedAtTime": "1970-01-01T00:16:40Z", + "id": "FUWbvfpTZbeG8W9w9dCd8R2RxXsx53EyYrGZJe7Qrqi6", + "proof": { + "creator": "did:piprate:GmNuLkqi3NY9h5PbeLqHBq", + "proofValue": "62R8ogF69UcgPyv11ABbcGW7G3zbbNdMtpoYYz5MkDLbMFGfdY6t4TAREdjNU5QHFT6TCeKB6eHSmJJME9jb6nPA", + "type": "Ed25519Signature2018" + }, + "resource": { + "contentType": "File", + "fingerprint": "3zNymhmMHiZqCD9Nvrg8rI9QZ05cCb7h01KOtCMEH9c=", + "fingerprintAlgorithm": "fingerprints:sha256", + "id": "did:piprate:7PDaLZGd459vFXwhKRv8btMppNjKtq7vKkUodF48csAD" + }, + "type": [ + "Impression", + "Entity", + "Bundle" + ], + "wasAttributedTo": "did:piprate:GmNuLkqi3NY9h5PbeLqHBq" + }, + "storage": [ + { + "asset": "did:piprate:7PDaLZGd459vFXwhKRv8btMppNjKtq7vKkUodF48csAD", + "id": "did:piprate:7PDaLZGd459vFXwhKRv8btMppNjKtq7vKkUodF48csAD", + "method": "memory", + "mimeType": "application/json", + "size": 301, + "type": "Resource", + "vault": "Z2kcCarCE47SDjtWD5ruyijsQyWMF5B1jjk6HHWngoe" + } + ], + "type": "Lease" +}` +) + +func NewExtendedKey(t *testing.T) *hdkeychain.ExtendedKey { + t.Helper() + + seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) + require.NoError(t, err) + privHD, _, err := model.GenerateNewHDKey(seed) + require.NoError(t, err) + + return privHD +} + +func CreateSampleLease(t *testing.T) *model.Lease { + t.Helper() + + var lease *model.Lease + require.NoError(t, jsonw.Decode(strings.NewReader(sampleLease1), &lease)) + return lease +} + +func GetFlowBalance(t *testing.T, se *splash.TemplateEngine, address flow.Address) float64 { + t.Helper() + + v, err := se.NewScript("account_balance_flow"). + Argument(cadence.NewAddress(address)). + RunReturns(context.Background()) + require.NoError(t, err) + + return splash.ToFloat64(v) +} + +func GetRecord(t *testing.T, se *splash.TemplateEngine, id string) *model.Record { + t.Helper() + + val, err := se.NewScript("metalocker_get_record"). + Argument(cadence.String(id)). + RunReturns(context.Background()) + require.NoError(t, err) + + res, err := RecordFromCadence(val) + require.NoError(t, err) + + return res +} + +func AssertDataAssetCounter(t *testing.T, se *splash.TemplateEngine, id string, counter uint64) { + t.Helper() + + val, err := se.NewScript("metalocker_get_data_asset_counter"). + Argument(cadence.String(id)). + RunReturns(context.Background()) + require.NoError(t, err) + + assert.Equal(t, counter, uint64(val.(cadence.UInt64))) +} + +func GetAssetHead(t *testing.T, se *splash.TemplateEngine, headID string) *model.Record { + t.Helper() + + val, err := se.NewScript("metalocker_get_asset_head"). + Argument(cadence.String(headID)). + RunReturns(context.Background()) + require.NoError(t, err) + + opt, _ := val.(cadence.Optional) + if opt.Value == nil { + return nil + } else { + rec, err := RecordFromCadence(opt.Value) + require.NoError(t, err) + + return rec + } +} diff --git a/ledger/onflow/contract_test.go b/ledger/onflow/contract_test.go new file mode 100644 index 0000000..ed8b511 --- /dev/null +++ b/ledger/onflow/contract_test.go @@ -0,0 +1,399 @@ +package onflow_test + +import ( + "encoding/base64" + "os" + "strings" + "testing" + "time" + + . "github.com/piprate/metalocker/ledger/onflow" + "github.com/piprate/metalocker/ledger/onflow/emulator" + "github.com/piprate/metalocker/model" + "github.com/piprate/metalocker/utils/jsonw" + "github.com/piprate/splash" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func init() { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Stamp}) +} + +const testRecordTemplate = ` +{ + "id": "47Gv8nkNgEr95gRZd98c4NAhi4wszmUBk6tytk4TtVCo", + "routingKey": "db5ryf4F6JQjD3aRqMF13RdueHkeHU9WcAx9dEVfy1mq", + "keyIndex": 4, + "operationType": 1, + "address": "CW1bDDn1Pp3W486rVxkrHKLUTirqytPMjBfFfXrqihU8", + "ac": "5z//FjtYNAChMv+8dpMqIiM4uKkGv0BXWx2yu6avIgE=", + "rc": "pVbAvIkXtnzmF7z3U6n4GVQwgjjhQgvw/bomh1Fm72o=", + "rcType": 1, + "dataAssets": [ + "Gp7ZX5S5NECqoMz3u7micC7TuhvWB9z8ErAkympVgt4A" + ], + "signature": "AN1rKvtoGVPioTYojunoBTWWx6MTdb8VHB8vN8takPu4fpe5K7LFowgqW8meuLwniFosM2xCBbHkdhqUoTovr4xDCzKF2JNZb" +} +` + +func TestMetaLocker_submitRecord_SequentialRecords(t *testing.T) { + conn, err := splash.NewInMemoryTestConnector(".", true) + require.NoError(t, err) + + emulator.ConfigureInMemoryEmulator(t, conn, adminAccountName, "10.0") + + se, err := NewTemplateEngine(conn) + require.NoError(t, err) + + var rec *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &rec)) + + nodeAcct := conn.Account(platformAccountName) + + emulator.FundAccountWithFlow(t, se, nodeAcct.Address, "10.0") + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(rec, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess(). + PrintEvents(). + AssertEventCount(8). + AssertPartialEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.BlockOpened", map[string]interface{}{ + "chain": "0x179b6b1cb6755e31", + "id": "2", + })). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordPublished", map[string]interface{}{ + "blockID": "2", + "id": rec.ID, + })) + + res := GetRecord(t, se, rec.ID) + + assert.Equal(t, model.StatusPublished, res.Status) + + AssertDataAssetCounter(t, se, rec.DataAssets[0], 1) + AssertDataAssetCounter(t, se, rec.OperationAddress, 1) + AssertDataAssetCounter(t, se, "non-existent-asset-id", 0) + + rec2 := rec.Copy() + rec2.ID = "second-record" + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(rec2, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess(). + AssertEventCount(8). + PrintEvents(). + AssertPartialEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.BlockOpened", map[string]interface{}{ + "chain": "0x179b6b1cb6755e31", + "id": "3", + })). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordPublished", map[string]interface{}{ + "blockID": "3", + "id": "second-record", + })) + + AssertDataAssetCounter(t, se, rec.DataAssets[0], 2) + AssertDataAssetCounter(t, se, rec.OperationAddress, 2) +} + +func TestMetaLocker_submitRecord_CollatedRecords(t *testing.T) { + conn, err := splash.NewInMemoryTestConnector(".", true) + require.NoError(t, err) + + emulator.ConfigureInMemoryEmulator(t, conn, adminAccountName, "10.0") + + se, err := NewTemplateEngine(conn) + require.NoError(t, err) + + var rec *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &rec)) + + nodeAcct := conn.Account(platformAccountName) + adminAcct := conn.Account(adminAccountName) + + emulator.FundAccountWithFlow(t, se, nodeAcct.Address, "10.0") + + conn.DisableAutoMine() + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(rec, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess(). + AssertEventCount(0) + + rec2 := rec.Copy() + rec2.ID = "second-record" + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(adminAcct.Name). + Argument(RecordToCadence(rec2, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess(). + AssertEventCount(0) + + _, resList, err := conn.ExecuteAndCommitBlock(t) + require.NoError(t, err) + require.Len(t, resList, 2) + + resList[0].RequireSuccess(). + AssertEventCount(8). + AssertPartialEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.BlockOpened", map[string]interface{}{ + "chain": "0x179b6b1cb6755e31", + "id": "2", + })). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordPublished", map[string]interface{}{ + "blockID": "2", + "id": rec.ID, + })) + + resList[1].RequireSuccess(). + PrintEvents(). + AssertEventCount(6). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordPublished", map[string]interface{}{ + "blockID": "2", + "id": "second-record", + })) + + res := GetRecord(t, se, rec.ID) + assert.Equal(t, model.StatusPublished, res.Status) + + AssertDataAssetCounter(t, se, rec.DataAssets[0], 2) + AssertDataAssetCounter(t, se, rec.OperationAddress, 2) +} + +func TestMetaLocker_submitRecord_LeaseRevocation(t *testing.T) { + conn, err := splash.NewInMemoryTestConnector(".", true) + require.NoError(t, err) + + emulator.ConfigureInMemoryEmulator(t, conn, adminAccountName, "10.0") + + se, err := NewTemplateEngine(conn) + require.NoError(t, err) + + privateHDKey := NewExtendedKey(t) + lease := CreateSampleLease(t) + leaseAddress := "CW1bDDn1Pp3W486rVxkrHKLUTirqytPMjBfFfXrqihU8" + keyIndex := model.RandomKeyIndex() + recPrivKey, err := privateHDKey.Derive(keyIndex) + require.NoError(t, err) + rec, err := model.BuildLeaseRecord(keyIndex, recPrivKey, lease, leaseAddress, false) + require.NoError(t, err) + + nodeAcct := conn.Account(platformAccountName) + + emulator.FundAccountWithFlow(t, se, nodeAcct.Address, "10.0") + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(rec, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess() + + res := GetRecord(t, se, rec.ID) + + assert.Equal(t, model.StatusPublished, res.Status) + + pk, err := privateHDKey.ECPrivKey() + require.NoError(t, err) + + routingKey, _ := model.BuildRoutingKey(pk.PubKey()) + + subjPrivKey, err := privateHDKey.Derive(rec.KeyIndex) + require.NoError(t, err) + + goodACInput := model.BuildAuthorisingCommitmentInput(subjPrivKey, rec.OperationAddress) + badACInput := model.BuildAuthorisingCommitmentInput(subjPrivKey, "another-op-address") + + revocationRec := &model.Record{ + RoutingKey: routingKey, + KeyIndex: 12345, + Operation: model.OpTypeLeaseRevocation, + SubjectRecord: rec.ID, + RevocationProof: []string{ + base64.StdEncoding.EncodeToString(badACInput), + }, + } + require.NoError(t, revocationRec.Seal(pk)) + require.NoError(t, revocationRec.Validate()) + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(revocationRec, se.ContractAddress("MetaLocker"))). + Test(t). + AssertFailure("invalid revocation proof") + + revocationRec.RevocationProof[0] = base64.StdEncoding.EncodeToString(goodACInput) + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(revocationRec, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess(). + AssertEventCount(9). + PrintEvents(). + AssertPartialEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.BlockOpened", map[string]interface{}{ + "chain": "0x179b6b1cb6755e31", + "id": "3", + })). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordPublished", map[string]interface{}{ + "blockID": "3", + "id": revocationRec.ID, + })). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordRevoked", map[string]interface{}{ + "blockID": "3", + "id": rec.ID, + })) + + res = GetRecord(t, se, rec.ID) + assert.Equal(t, model.StatusRevoked, res.Status) + + AssertDataAssetCounter(t, se, rec.DataAssets[0], 0) + AssertDataAssetCounter(t, se, rec.OperationAddress, 0) +} + +func TestMetaLocker_submitRecord_AssetHead(t *testing.T) { + conn, err := splash.NewInMemoryTestConnector(".", true) + require.NoError(t, err) + + emulator.ConfigureInMemoryEmulator(t, conn, adminAccountName, "10.0") + + se, err := NewTemplateEngine(conn) + require.NoError(t, err) + + privateHDKey := NewExtendedKey(t) + lease := CreateSampleLease(t) + leaseAddress := "CW1bDDn1Pp3W486rVxkrHKLUTirqytPMjBfFfXrqihU8" + + keyIndex := model.RandomKeyIndex() + recPrivKey, err := privateHDKey.Derive(keyIndex) + require.NoError(t, err) + + rec, err := model.BuildLeaseRecord(keyIndex, recPrivKey, lease, leaseAddress, false) + require.NoError(t, err) + + nodeAcct := conn.Account(platformAccountName) + + emulator.FundAccountWithFlow(t, se, nodeAcct.Address, "10.0") + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(rec, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess() + + res := GetRecord(t, se, rec.ID) + + assert.Equal(t, model.StatusPublished, res.Status) + + pk, err := privateHDKey.ECPrivKey() + require.NoError(t, err) + + //recPrivKey, err := privateHDKey.Derive(rec.KeyIndex) + //require.NoError(t, err) + + headName := "testHead" + senderID := "did:piprate:sender-id" + lockerID := "testLockerID" + assetID := "did:piprate:FdywEGiR5nubMFZgbfneUdAx5V858eaiRcBGRGVpj3Y6" + sharedSecret := "NYGupZJZfxotkGaLGKpuqOX8K7xVvE7qEDvNgfru4e8=" //nolint:gosec + + assetHeadRec1, err := model.BuildAssetHeadRecord(senderID, privateHDKey, lockerID, sharedSecret, assetID, headName, rec.ID, nil) + require.NoError(t, err) + + assert.Empty(t, GetAssetHead(t, se, assetHeadRec1.HeadID)) + + // try updating a head that doesn't exist yet by setting SubjectRecord. Should fail. + + assetHeadRec1.SubjectRecord = "wrong-previous-head-record-id" + assetHeadRec1.RevocationProof = []string{"dummy-proof"} + require.NoError(t, assetHeadRec1.Seal(pk)) + require.NoError(t, assetHeadRec1.Validate()) + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(assetHeadRec1, se.ContractAddress("MetaLocker"))). + Test(t). + AssertFailure("invalid reference to previous head") + + // set a new asset head. Should succeed. + + assetHeadRec1.SubjectRecord = "" + assetHeadRec1.RevocationProof = nil + require.NoError(t, assetHeadRec1.Seal(pk)) + require.NoError(t, assetHeadRec1.Validate()) + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(assetHeadRec1, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess(). + AssertEventCount(8). + //PrintEvents(). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordPublished", map[string]interface{}{ + "blockID": "3", + "id": assetHeadRec1.ID, + })) + + headRec1 := GetAssetHead(t, se, assetHeadRec1.HeadID) + assert.NotEmpty(t, headRec1) + + // build a record to update the asset head + + assetHeadRec2, err := model.BuildAssetHeadRecord(senderID, privateHDKey, lockerID, sharedSecret, assetID, headName, rec.ID, assetHeadRec1) + require.NoError(t, err) + + // try updating the head with invalid proof. Should fail. + + goodProof := assetHeadRec2.RevocationProof[0] + + badACInput := model.BuildAuthorisingCommitmentInput(recPrivKey, "another-op-address") + assetHeadRec2.RevocationProof[0] = base64.StdEncoding.EncodeToString(badACInput) + require.NoError(t, assetHeadRec2.Seal(pk)) + require.NoError(t, assetHeadRec2.Validate()) + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(assetHeadRec2, se.ContractAddress("MetaLocker"))). + Test(t). + AssertFailure("asset head revocation failed: invalid proof") + + // update the head with valid proof + + assetHeadRec2.RevocationProof[0] = goodProof + require.NoError(t, assetHeadRec2.Seal(pk)) + require.NoError(t, assetHeadRec2.Validate()) + + _ = se.NewTransaction("metalocker_submit_record"). + SignProposeAndPayAs(nodeAcct.Name). + Argument(RecordToCadence(assetHeadRec2, se.ContractAddress("MetaLocker"))). + Test(t). + RequireSuccess(). + AssertEventCount(9). + //PrintEvents(). + AssertPartialEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.BlockOpened", map[string]interface{}{ + "chain": "0x179b6b1cb6755e31", + "id": "4", + })). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordPublished", map[string]interface{}{ + "blockID": "4", + "id": assetHeadRec2.ID, + })). + AssertEmitEvent(splash.NewTestEvent("A.179b6b1cb6755e31.MetaLocker.RecordRevoked", map[string]interface{}{ + "blockID": "4", + "id": assetHeadRec1.ID, + })) + + res = GetRecord(t, se, assetHeadRec1.ID) + assert.Equal(t, model.StatusRevoked, res.Status) + + headRec2 := GetAssetHead(t, se, assetHeadRec2.HeadID) + assert.NotEmpty(t, headRec2) + assert.NotEqual(t, headRec1, headRec2) +} diff --git a/ledger/onflow/contracts/MetaLocker.cdc b/ledger/onflow/contracts/MetaLocker.cdc new file mode 100644 index 0000000..1319830 --- /dev/null +++ b/ledger/onflow/contracts/MetaLocker.cdc @@ -0,0 +1,474 @@ +// MetaLocker is a data distribution framework that enables secure and privacy preserving data exchange between participants of a knowledge network. +// +// Source: https://github.com/piprate/metalocker/ledger/onflow/contracts +// +access(all) +contract MetaLocker { + + // Events + // + access(all) + event MetaLockerInitialized() + access(all) + event BlockOpened(chain: Address, id: UInt64, parentHash: String) + access(all) + event BlockConfirmed(chain: Address, id: UInt64, parentHash: String, nonce: String, recordCount: UInt32, hash: String) + access(all) + event RecordPublished(id: String, blockID: UInt64) + access(all) + event RecordRevoked(id: String, blockID: UInt64) + + access(self) + var blocks: {UInt64: MetaBlock} + + access(self) + var records: {String: Record} + + access(self) + var dataAssetCounters: {String: UInt64} + + access(self) + var assetHeads: {String: String} + + access(self) + var prevBlockHash: [UInt8] + access(self) + var currentBlockBody: [UInt8] + access(self) + var currentRecordCount: UInt32 + access(self) + var currentBlockNumber: UInt64 + access(self) + var genesisBlockHeight: UInt64 + access(self) + var currentBlockHeight: UInt64 + + // Record represents one data transaction (lease, revocation, etc). + // It contains no details which would allow a third party observer to identify + // the participants or the nature of this transaction. + // + access(all) + struct Record { + // ID of the record. Currently it's a hash of the record generated + // by the Seal function (see below). + access(all) + let id: String + + // RoutingKey is a public key from the locker HD structure. It can be + // used to filter specific messages from the ledger. + access(all) + let routingKey: String + + // Index of the HD key used to produce the routing key. + access(all) + let keyIndex: UInt32 + + // Type of the operation. May be removed from the record in the future. + access(all) + let operationType: UInt32 + + // OperationAddress is the address of the Operation (can be an asset ID, IPFS address, etc.) + access(all) + let address: String + + // Flags contain a set of flags that modify the record's behaviour. + // See RecordFlagXXX constants for examples. + access(all) + let flags: UInt32 + + // AuthorisingCommitment is binary data which allows the originator + // of the transaction to prove their role, without disclosing any + // other information about this transaction + access(all) + let ac: [UInt8] + + // AuthorisingCommitmentType. for future use: there may be different + // types of commitment structures. + access(all) + let acType: UInt8 + + // RequestingCommitment is binary data which allows the recipient of + // the transaction to prove their right to access data without disclosing + // any other information about this transaction + access(all) + let rc: [UInt8] + + // RequestingCommitmentType. for future use: there may be different types + // of commitment structures. + access(all) + let rcType: UInt8 + + // ImpressionCommitment is binary data which allows a party to prove + // that this record contains a specific impression, by combining + // the impression ID with another artifact, a trapdoor. + access(all) + let ic: [UInt8] + + // ImpressionCommitmentType. for future use: there may be different + // types of commitment structures. + access(all) + let icType: UInt8 + + // DataAssets is a list of data assets (blobs) attached to the record + access(all) + let dataAssets: [String] + + // Lease Revocation fields + + access(all) + let subjectRecord: String + access(all) + let revocationProof: [[UInt8]] + + // Head fields + + // HeadID is unique ID of the asset head. + access(all) + let headID: String + // HeadBody contains base64-encoded, encrypted head body. + access(all) + let headBody: String + + // Signature contains a digital signature of the record, signed by + // the record's private HD key + access(all) + let signature: String + + access(all) + var status: String + access(all) + var blockNumber: UInt64 + + init(id: String, routingKey: String, keyIndex: UInt32, operationType: UInt32, address: String, + flags: UInt32, ac: [UInt8], acType: UInt8, rc: [UInt8], rcType: UInt8, ic: [UInt8], + icType: UInt8, dataAssets: [String], subjectRecord: String, revocationProof: [[UInt8]], + headID: String, headBody: String, signature: String, status: String, blockNumber: UInt64) { + self.id = id + self.routingKey = routingKey + self.keyIndex = keyIndex + self.operationType = operationType + self.address = address + self.flags = flags + self.ac = ac + self.acType = acType + self.rc = rc + self.rcType = rcType + self.ic = ic + self.icType = icType + self.dataAssets = dataAssets + self.subjectRecord = subjectRecord + self.revocationProof = revocationProof + self.headID = headID + self.headBody = headBody + self.signature = signature + self.status = status + self.blockNumber = 0 + } + + access(contract) + fun setStatus(val: String) { + self.status = val + } + + access(contract) + fun setBlockNumber(val: UInt64) { + self.blockNumber = val + } + } + + access(all) + struct MetaBlock { + access(all) + let number: UInt64 + access(all) + var nonce: String + access(all) + var hash: String + access(all) + var parentHash: String + access(all) + var status: UInt8 + access(all) + var records: [[String]] + + init(number: UInt64, parentHash: String, status: UInt8) { + self.number = number + self.nonce = "" + self.hash = "" + self.parentHash = parentHash + self.status = status + self.records = [] + } + + access(contract) + fun setStatus(val: UInt8) { + self.status = val + } + + access(contract) + fun confirm(nonce: String, hash: String) { + self.nonce = nonce + self.hash = hash + self.status = 2 + } + + access(contract) + fun addRecord(id: String, routingKey: String, keyIndex: String) { + self.records.append([id, routingKey, keyIndex]) + } + } + + access(contract) + fun rollBlock(blockHeight: UInt64) { + let nonce = revertibleRandom().toBigEndianBytes() + let currentRecordCount = UInt32(self.records.length) + let base = self.prevBlockHash + .concat(nonce) + .concat(currentRecordCount.toBigEndianBytes()) + .concat(self.currentBlockBody) + let hash = HashAlgorithm.SHA2_256.hash(base) + + self.blocks[self.currentBlockNumber]!.confirm(nonce: String.encodeHex(nonce), hash: String.encodeHex(hash)) + + let block = self.blocks[self.currentBlockNumber]! + + emit BlockConfirmed( + chain: self.account.address, + id: self.currentBlockNumber, + parentHash: block.parentHash, + nonce: block.nonce, + recordCount: UInt32(block.records.length), + hash: block.hash) + + self.currentRecordCount = 0 + self.currentBlockBody = [] + self.currentBlockHeight = blockHeight + self.currentBlockNumber = self.currentBlockNumber + UInt64(1) + self.prevBlockHash = hash + + self.blocks[self.currentBlockNumber] = MetaBlock(number: self.currentBlockNumber, parentHash: String.encodeHex(self.prevBlockHash), status: 1) + emit BlockOpened( + chain: self.account.address, + id: self.currentBlockNumber, + parentHash: String.encodeHex(self.prevBlockHash)) + } + + access(all) + fun submitRecord(record: Record) { + pre { + record.id != "" : "empty record ID" + record.signature != "" : "unsigned record" + } + + if self.records.containsKey(record.id) { + panic("record already exists") + } + + let newBlockHeight = getCurrentBlock().height + + if newBlockHeight > self.currentBlockHeight { + self.rollBlock(blockHeight: newBlockHeight) + } + + switch record.operationType { + case UInt32(1): // lease + self.submitLease(record: record, blockNumber: self.currentBlockNumber) + case UInt32(2): // lease revocation + self.submitLeaseRevocation(record: record, blockNumber: self.currentBlockNumber) + case UInt32(3): // set asset head + self.submitAssetHead(record: record, blockNumber: self.currentBlockNumber) + default: + panic("unsupported operation type: ".concat(record.operationType.toString())) + } + + self.blocks[self.currentBlockNumber]!.addRecord(id: record.id, routingKey: record.routingKey, keyIndex: record.keyIndex.toString()) + self.currentBlockBody = self.currentBlockBody.concat(record.id.utf8) + emit RecordPublished(id: record.id, blockID: self.currentBlockNumber) + } + + access(contract) + fun submitLease(record: Record, blockNumber: UInt64) { + //log(record) + + record.setStatus(val: "published") + record.setBlockNumber(val: blockNumber) + self.records[record.id] = record + + // update data asset counters + + let incrementCounter = fun (dataAssetID: String) { + var counter = self.dataAssetCounters[dataAssetID] + if counter != nil { + self.dataAssetCounters[dataAssetID] = counter! + 1 + } else { + self.dataAssetCounters[dataAssetID] = 1 + } + } + + for id in record.dataAssets { + incrementCounter(id) + } + incrementCounter(record.address) + } + + access(contract) + fun submitLeaseRevocation(record: Record, blockNumber: UInt64) { + let subjRec = self.records[record.subjectRecord] + ?? panic("Subject record not found: ".concat(record.subjectRecord)) + + // check revocation proof + + assert(record.revocationProof.length > 0, message: "bad revocation proof format") + let digest = HashAlgorithm.SHA2_256.hash(record.revocationProof[0]) + assert(digest.length == subjRec.ac.length, message: "wrong digest size") + + var match = true + for idx, val in digest { + if subjRec.ac[idx] != val { + match = false + break + } + } + assert(match, message: "invalid revocation proof") + + // update data asset counters + + let decrementCounter = fun (dataAssetID: String) { + var counter = self.dataAssetCounters[dataAssetID] + if counter != nil { + self.dataAssetCounters[dataAssetID] = counter! - 1 + } + } + + for id in subjRec.dataAssets { + decrementCounter(id) + } + decrementCounter(subjRec.address) + + // update subject record state + + subjRec.setStatus(val: "revoked") + self.records[record.subjectRecord] = subjRec + + record.setStatus(val: "published") + record.setBlockNumber(val: blockNumber) + self.records[record.id] = record + + emit RecordRevoked(id: subjRec.id, blockID: self.currentBlockNumber) + } + + access(contract) + fun submitAssetHead(record: Record, blockNumber: UInt64) { + let prevHeadRecordID = self.assetHeads[record.headID] + + if record.subjectRecord != "" { + assert(prevHeadRecordID != nil, message: "invalid reference to previous head") + assert(prevHeadRecordID! == record.subjectRecord, message: "previous head mismatch") + assert(record.revocationProof.length > 0, message: "bad revocation proof format") + + let subjRec = self.records[record.subjectRecord] + ?? panic("previous asset head record not found: ".concat(record.subjectRecord)) + + // check revocation proof + + let digest = HashAlgorithm.SHA2_256.hash(record.revocationProof[0]) + assert(digest.length == subjRec.ac.length, message: "wrong digest size") + + var match = true + for idx, val in digest { + if subjRec.ac[idx] != val { + match = false + break + } + } + assert(match, message: "asset head revocation failed: invalid proof") + + // update subject record state + subjRec.setStatus(val: "revoked") + self.records[record.subjectRecord] = subjRec + + emit RecordRevoked(id: subjRec.id, blockID: self.currentBlockNumber) + } else { + assert(prevHeadRecordID == nil, message: "missing reference to previous head") + } + + record.setStatus(val: "published") + record.setBlockNumber(val: blockNumber) + self.records[record.id] = record + self.assetHeads[record.headID] = record.id + } + + access(all) + fun getRecord(id: String): Record? { + return self.records[id] + } + + access(all) + fun getDataAssetCounter(id: String): UInt64 { + let counter = self.dataAssetCounters[id] + if counter != nil { + return counter! + } else { + return 0 + } + } + + access(all) + fun getAssetHead(headID: String): Record? { + let rid = self.assetHeads[headID] + if rid != nil { + return self.records[rid!] + } else { + return nil + } + } + + access(all) + fun getBlock(number: UInt64): MetaBlock? { + return self.blocks[number] + } + + access(all) + fun getBlockRecords(blockNumber: UInt64): [String] { + return [] + } + + access(all) + fun getTopBlock(): UInt64 { + return self.currentBlockNumber + } + + access(all) + fun getGenesisBlockHeight(): UInt64 { + return self.genesisBlockHeight + } + + access(all) + fun getTopBlockHeight(): UInt64 { + return self.currentBlockHeight + } + + // initializer + // + init() { + self.blocks = {} + self.records = {} + self.dataAssetCounters = {} + self.assetHeads = {} + self.prevBlockHash = [] + self.currentBlockNumber = 0 + self.currentRecordCount = 0 + self.currentBlockBody = [] + self.currentBlockHeight = getCurrentBlock().height + self.genesisBlockHeight = self.currentBlockHeight + + emit MetaLockerInitialized() + + // create genesis block + + self.blocks[self.currentBlockNumber] = MetaBlock(number: self.currentBlockNumber, parentHash: "", status: 1) + emit BlockOpened(chain: self.account.address, id: self.currentBlockNumber, parentHash: "") + self.rollBlock(blockHeight: self.currentBlockHeight) + //emit BlockConfirmed(chain: self.account.address, id: 0, parentHash: "", nonce: "", recordCount: 0, hash: "") + } +} diff --git a/ledger/onflow/contracts/standard/Burner.cdc b/ledger/onflow/contracts/standard/Burner.cdc new file mode 100644 index 0000000..1e509a6 --- /dev/null +++ b/ledger/onflow/contracts/standard/Burner.cdc @@ -0,0 +1,50 @@ +/// Burner is a contract that can facilitate the destruction of any resource on flow. +/// +/// Contributors +/// - Austin Kline - https://twitter.com/austin_flowty +/// - Deniz Edincik - https://twitter.com/bluesign +/// - Bastian Müller - https://twitter.com/turbolent +access(all) contract Burner { + /// When Crescendo (Cadence 1.0) is released, custom destructors will be removed from cadece. + /// Burnable is an interface meant to replace this lost feature, allowing anyone to add a callback + /// method to ensure they do not destroy something which is not meant to be, + /// or to add logic based on destruction such as tracking the supply of a FT Collection + /// + /// NOTE: The only way to see benefit from this interface + /// is to always use the burn method in this contract. Anyone who owns a resource can always elect **not** + /// to destroy a resource this way + access(all) resource interface Burnable { + access(contract) fun burnCallback() + } + + /// burn is a global method which will destroy any resource it is given. + /// If the provided resource implements the Burnable interface, + /// it will call the burnCallback method and then destroy afterwards. + access(all) fun burn(_ toBurn: @AnyResource?) { + if toBurn == nil { + destroy toBurn + return + } + let r <- toBurn! + + if let s <- r as? @{Burnable} { + s.burnCallback() + destroy s + } else if let arr <- r as? @[AnyResource] { + while arr.length > 0 { + let item <- arr.removeFirst() + self.burn(<-item) + } + destroy arr + } else if let dict <- r as? @{HashableStruct: AnyResource} { + let keys = dict.keys + while keys.length > 0 { + let item <- dict.remove(key: keys.removeFirst())! + self.burn(<-item) + } + destroy dict + } else { + destroy r + } + } +} diff --git a/ledger/onflow/contracts/standard/FlowToken.cdc b/ledger/onflow/contracts/standard/FlowToken.cdc new file mode 100644 index 0000000..835cb1d --- /dev/null +++ b/ledger/onflow/contracts/standard/FlowToken.cdc @@ -0,0 +1,297 @@ +import FungibleToken from "./FungibleToken.cdc" +import MetadataViews from "./MetadataViews.cdc" +import FungibleTokenMetadataViews from "./FungibleTokenMetadataViews.cdc" + +access(all) contract FlowToken: FungibleToken { + + // Total supply of Flow tokens in existence + access(all) var totalSupply: UFix64 + + // Event that is emitted when tokens are withdrawn from a Vault + access(all) event TokensWithdrawn(amount: UFix64, from: Address?) + + // Event that is emitted when tokens are deposited to a Vault + access(all) event TokensDeposited(amount: UFix64, to: Address?) + + // Event that is emitted when new tokens are minted + access(all) event TokensMinted(amount: UFix64) + + // Event that is emitted when a new minter resource is created + access(all) event MinterCreated(allowedAmount: UFix64) + + // Event that is emitted when a new burner resource is created + access(all) event BurnerCreated() + + // Vault + // + // Each user stores an instance of only the Vault in their storage + // The functions in the Vault and governed by the pre and post conditions + // in FungibleToken when they are called. + // The checks happen at runtime whenever a function is called. + // + // Resources can only be created in the context of the contract that they + // are defined in, so there is no way for a malicious user to create Vaults + // out of thin air. A special Minter resource needs to be defined to mint + // new tokens. + // + access(all) resource Vault: FungibleToken.Vault { + + // holds the balance of a users tokens + access(all) var balance: UFix64 + + // initialize the balance at resource creation time + init(balance: UFix64) { + self.balance = balance + } + + /// Called when a fungible token is burned via the `Burner.burn()` method + access(contract) fun burnCallback() { + if self.balance > 0.0 { + FlowToken.totalSupply = FlowToken.totalSupply - self.balance + } + self.balance = 0.0 + } + + /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts + access(all) view fun getSupportedVaultTypes(): {Type: Bool} { + return {self.getType(): true} + } + + access(all) view fun isSupportedVaultType(type: Type): Bool { + if (type == self.getType()) { return true } else { return false } + } + + /// Asks if the amount can be withdrawn from this vault + access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool { + return amount <= self.balance + } + + // withdraw + // + // Function that takes an integer amount as an argument + // and withdraws that amount from the Vault. + // It creates a new temporary Vault that is used to hold + // the money that is being transferred. It returns the newly + // created Vault to the context that called so it can be deposited + // elsewhere. + // + access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} { + self.balance = self.balance - amount + + // If the owner is the staking account, do not emit the contract defined events + // this is to help with the performance of the epoch transition operations + // Either way, event listeners should be paying attention to the + // FungibleToken.Withdrawn events anyway because those contain + // much more comprehensive metadata + // Additionally, these events will eventually be removed from this contract completely + // in favor of the FungibleToken events + if let address = self.owner?.address { + if address != 0xf8d6e0586b0a20c7 && + address != 0xf4527793ee68aede && + address != 0x9eca2b38b18b5dfe && + address != 0x8624b52f9ddcd04a + { + emit TokensWithdrawn(amount: amount, from: address) + } + } else { + emit TokensWithdrawn(amount: amount, from: nil) + } + return <-create Vault(balance: amount) + } + + // deposit + // + // Function that takes a Vault object as an argument and adds + // its balance to the balance of the owners Vault. + // It is allowed to destroy the sent Vault because the Vault + // was a temporary holder of the tokens. The Vault's balance has + // been consumed and therefore can be destroyed. + access(all) fun deposit(from: @{FungibleToken.Vault}) { + let vault <- from as! @FlowToken.Vault + self.balance = self.balance + vault.balance + + // If the owner is the staking account, do not emit the contract defined events + // this is to help with the performance of the epoch transition operations + // Either way, event listeners should be paying attention to the + // FungibleToken.Deposited events anyway because those contain + // much more comprehensive metadata + // Additionally, these events will eventually be removed from this contract completely + // in favor of the FungibleToken events + if let address = self.owner?.address { + if address != 0xf8d6e0586b0a20c7 && + address != 0xf4527793ee68aede && + address != 0x9eca2b38b18b5dfe && + address != 0x8624b52f9ddcd04a + { + emit TokensDeposited(amount: vault.balance, to: address) + } + } else { + emit TokensDeposited(amount: vault.balance, to: nil) + } + vault.balance = 0.0 + destroy vault + } + + /// Get all the Metadata Views implemented by FlowToken + /// + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + access(all) view fun getViews(): [Type]{ + return FlowToken.getContractViews(resourceType: nil) + } + + /// Get a Metadata View from FlowToken + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveView(_ view: Type): AnyStruct? { + return FlowToken.resolveContractView(resourceType: nil, viewType: view) + } + + access(all) fun createEmptyVault(): @{FungibleToken.Vault} { + return <-create Vault(balance: 0.0) + } + } + + // createEmptyVault + // + // Function that creates a new Vault with a balance of zero + // and returns it to the calling context. A user must call this function + // and store the returned Vault in their storage in order to allow their + // account to be able to receive deposits of this token type. + // + access(all) fun createEmptyVault(vaultType: Type): @FlowToken.Vault { + return <-create Vault(balance: 0.0) + } + + /// Gets a list of the metadata views that this contract supports + access(all) view fun getContractViews(resourceType: Type?): [Type] { + return [Type(), + Type(), + Type(), + Type()] + } + + /// Get a Metadata View from FlowToken + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { + switch viewType { + case Type(): + return FungibleTokenMetadataViews.FTView( + ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTDisplay?, + ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ) + case Type(): + let media = MetadataViews.Media( + file: MetadataViews.HTTPFile( + url: FlowToken.getLogoURI() + ), + mediaType: "image/svg+xml" + ) + let medias = MetadataViews.Medias([media]) + return FungibleTokenMetadataViews.FTDisplay( + name: "FLOW Network Token", + symbol: "FLOW", + description: "FLOW is the native token for the Flow blockchain. It is required for securing the network, transaction fees, storage fees, staking, FLIP voting and may be used by applications built on the Flow Blockchain", + externalURL: MetadataViews.ExternalURL("https://flow.com"), + logos: medias, + socials: { + "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") + } + ) + case Type(): + let vaultRef = FlowToken.account.storage.borrow(from: /storage/flowTokenVault) + ?? panic("Could not borrow reference to the contract's Vault!") + return FungibleTokenMetadataViews.FTVaultData( + storagePath: /storage/flowTokenVault, + receiverPath: /public/flowTokenReceiver, + metadataPath: /public/flowTokenBalance, + receiverLinkedType: Type<&{FungibleToken.Receiver, FungibleToken.Vault}>(), + metadataLinkedType: Type<&{FungibleToken.Balance, FungibleToken.Vault}>(), + createEmptyVaultFunction: (fun (): @{FungibleToken.Vault} { + return <-vaultRef.createEmptyVault() + }) + ) + case Type(): + return FungibleTokenMetadataViews.TotalSupply(totalSupply: FlowToken.totalSupply) + } + return nil + } + + access(all) resource Administrator { + // createNewMinter + // + // Function that creates and returns a new minter resource + // + access(all) fun createNewMinter(allowedAmount: UFix64): @Minter { + emit MinterCreated(allowedAmount: allowedAmount) + return <-create Minter(allowedAmount: allowedAmount) + } + } + + // Minter + // + // Resource object that token admin accounts can hold to mint new tokens. + // + access(all) resource Minter { + + // the amount of tokens that the minter is allowed to mint + access(all) var allowedAmount: UFix64 + + // mintTokens + // + // Function that mints new tokens, adds them to the total supply, + // and returns them to the calling context. + // + access(all) fun mintTokens(amount: UFix64): @FlowToken.Vault { + pre { + amount > UFix64(0): "Amount minted must be greater than zero" + amount <= self.allowedAmount: "Amount minted must be less than the allowed amount" + } + FlowToken.totalSupply = FlowToken.totalSupply + amount + self.allowedAmount = self.allowedAmount - amount + emit TokensMinted(amount: amount) + return <-create Vault(balance: amount) + } + + init(allowedAmount: UFix64) { + self.allowedAmount = allowedAmount + } + } + + /// Gets the Flow Logo XML URI from storage + access(all) view fun getLogoURI(): String { + return FlowToken.account.storage.copy(from: /storage/flowTokenLogoURI) ?? "" + } + + init() { + self.totalSupply = 0.0 + + // Create the Vault with the total supply of tokens and save it in storage + // + let vault <- create Vault(balance: self.totalSupply) + + self.account.storage.save(<-vault, to: /storage/flowTokenVault) + + // Create a public capability to the stored Vault that only exposes + // the `deposit` method through the `Receiver` interface + // + let receiverCapability = self.account.capabilities.storage.issue<&FlowToken.Vault>(/storage/flowTokenVault) + self.account.capabilities.publish(receiverCapability, at: /public/flowTokenReceiver) + + // Create a public capability to the stored Vault that only exposes + // the `balance` field through the `Balance` interface + // + let balanceCapability = self.account.capabilities.storage.issue<&FlowToken.Vault>(/storage/flowTokenVault) + self.account.capabilities.publish(balanceCapability, at: /public/flowTokenBalance) + + let admin <- create Administrator() + self.account.storage.save(<-admin, to: /storage/flowTokenAdmin) + + } +} diff --git a/ledger/onflow/contracts/standard/FungibleToken.cdc b/ledger/onflow/contracts/standard/FungibleToken.cdc new file mode 100644 index 0000000..cb619bb --- /dev/null +++ b/ledger/onflow/contracts/standard/FungibleToken.cdc @@ -0,0 +1,240 @@ +/** + +# The Flow Fungible Token standard + +## `FungibleToken` contract interface + +The interface that all Fungible Token contracts would have to conform to. +If a users wants to deploy a new token contract, their contract +would need to implement the FungibleToken interface. + +Their contract would have to follow all the rules and naming +that the interface specifies. + +## `Vault` resource + +Each account that owns tokens would need to have an instance +of the Vault resource stored in their account storage. + +The Vault resource has methods that the owner and other users can call. + +## `Provider`, `Receiver`, and `Balance` resource interfaces + +These interfaces declare pre-conditions and post-conditions that restrict +the execution of the functions in the Vault. + +They are separate because it gives the user the ability to share +a reference to their Vault that only exposes the fields functions +in one or more of the interfaces. + +It also gives users the ability to make custom resources that implement +these interfaces to do various things with the tokens. +For example, a faucet can be implemented by conforming +to the Provider interface. + +By using resources and interfaces, users of Fungible Token contracts +can send and receive tokens peer-to-peer, without having to interact +with a central ledger smart contract. To send tokens to another user, +a user would simply withdraw the tokens from their Vault, then call +the deposit function on another user's Vault to complete the transfer. + +*/ + +/// The interface that Fungible Token contracts implement. +/// +access(all) contract interface FungibleToken { + + // An entitlement for allowing the withdrawal of tokens from a Vault + access(all) entitlement Withdrawable + + /// The total number of tokens in existence. + /// It is up to the implementer to ensure that the total supply + /// stays accurate and up to date + access(all) var totalSupply: UFix64 + + /// The event that is emitted when the contract is created + access(all) event TokensInitialized(initialSupply: UFix64) + + /// The event that is emitted when tokens are withdrawn from a Vault + access(all) event TokensWithdrawn(amount: UFix64, from: Address?) + + /// The event that is emitted when tokens are deposited into a Vault + access(all) event TokensDeposited(amount: UFix64, to: Address?) + + /// The interface that enforces the requirements for withdrawing + /// tokens from the implementing type. + /// + /// It does not enforce requirements on `balance` here, + /// because it leaves open the possibility of creating custom providers + /// that do not necessarily need their own balance. + /// + access(all) resource interface Provider { + + /// Subtracts tokens from the owner's Vault + /// and returns a Vault with the removed tokens. + /// + /// The function's access level is public, but this is not a problem + /// because only the owner storing the resource in their account + /// can initially call this function. + /// + /// The owner may grant other accounts access by creating a private + /// capability that allows specific other users to access + /// the provider resource through a reference. + /// + /// The owner may also grant all accounts access by creating a public + /// capability that allows all users to access the provider + /// resource through a reference. + /// + /// @param amount: The amount of tokens to be withdrawn from the vault + /// @return The Vault resource containing the withdrawn funds + /// + access(Withdrawable) fun withdraw(amount: UFix64): @Vault { + post { + // `result` refers to the return value + result.balance == amount: + "Withdrawal amount must be the same as the balance of the withdrawn Vault" + } + } + } + + /// The interface that enforces the requirements for depositing + /// tokens into the implementing type. + /// + /// We do not include a condition that checks the balance because + /// we want to give users the ability to make custom receivers that + /// can do custom things with the tokens, like split them up and + /// send them to different places. + /// + access(all) resource interface Receiver { + + /// Takes a Vault and deposits it into the implementing resource type + /// + /// @param from: The Vault resource containing the funds that will be deposited + /// + access(all) fun deposit(from: @Vault) + + /// Below is referenced from the FLIP #69 https://github.com/onflow/flips/blob/main/flips/20230206-fungible-token-vault-type-discovery.md + /// + /// Returns the dictionary of Vault types that the the receiver is able to accept in its `deposit` method + /// this then it would return `{Type<@FlowToken.Vault>(): true}` and if any custom receiver + /// uses the default implementation then it would return empty dictionary as its parent + /// resource doesn't conform with the `FungibleToken.Vault` resource. + /// + /// Custom receiver implementations are expected to upgrade their contracts to add an implementation + /// that supports this method because it is very valuable for various applications to have. + /// + /// @return dictionary of supported deposit vault types by the implementing resource. + /// + access(all) view fun getSupportedVaultTypes(): {Type: Bool} { + // Below check is implemented to make sure that run-time type would + // only get returned when the parent resource conforms with `FungibleToken.Vault`. + if self.getType().isSubtype(of: Type<@FungibleToken.Vault>()) { + return {self.getType(): true} + } else { + // Return an empty dictionary as the default value for resource who don't + // implement `FungibleToken.Vault`, such as `FungibleTokenSwitchboard`, `TokenForwarder` etc. + return {} + } + } + } + + /// The interface that contains the `balance` field of the Vault + /// and enforces that when new Vaults are created, the balance + /// is initialized correctly. + /// + access(all) resource interface Balance { + + /// The total balance of a vault + /// + access(all) var balance: UFix64 + + init(balance: UFix64) { + post { + self.balance == balance: + "Balance must be initialized to the initial balance" + } + } + + /// Function that returns all the Metadata Views implemented by a Fungible Token + /// + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + access(all) view fun getViews(): [Type] { + return [] + } + + /// Function that resolves a metadata view for this fungible token by type. + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveView(_ view: Type): AnyStruct? { + return nil + } + } + + /// The resource that contains the functions to send and receive tokens. + /// The declaration of a concrete type in a contract interface means that + /// every Fungible Token contract that implements the FungibleToken interface + /// must define a concrete `Vault` resource that conforms to the `Provider`, `Receiver`, + /// and `Balance` interfaces, and declares their required fields and functions + /// + access(all) resource Vault: Provider, Receiver, Balance { + + /// The total balance of the vault + access(all) var balance: UFix64 + + // The conforming type must declare an initializer + // that allows providing the initial balance of the Vault + // + init(balance: UFix64) + + /// Subtracts `amount` from the Vault's balance + /// and returns a new Vault with the subtracted balance + /// + /// @param amount: The amount of tokens to be withdrawn from the vault + /// @return The Vault resource containing the withdrawn funds + /// + access(Withdrawable) fun withdraw(amount: UFix64): @Vault { + pre { + self.balance >= amount: + "Amount withdrawn must be less than or equal than the balance of the Vault" + } + post { + // use the special function `before` to get the value of the `balance` field + // at the beginning of the function execution + // + self.balance == before(self.balance) - amount: + "New Vault balance must be the difference of the previous balance and the withdrawn Vault" + } + } + + /// Takes a Vault and deposits it into the implementing resource type + /// + /// @param from: The Vault resource containing the funds that will be deposited + /// + access(all) fun deposit(from: @Vault) { + // Assert that the concrete type of the deposited vault is the same + // as the vault that is accepting the deposit + pre { + from.isInstance(self.getType()): + "Cannot deposit an incompatible token type" + } + post { + self.balance == before(self.balance) + before(from.balance): + "New Vault balance must be the sum of the previous balance and the deposited Vault" + } + } + } + + /// Allows any user to create a new Vault that has a zero balance + /// + /// @return The new Vault resource + /// + access(all) fun createEmptyVault(): @Vault { + post { + result.balance == 0.0: "The newly created Vault must have zero balance" + } + } +} diff --git a/ledger/onflow/contracts/standard/FungibleTokenMetadataViews.cdc b/ledger/onflow/contracts/standard/FungibleTokenMetadataViews.cdc new file mode 100644 index 0000000..98e62ca --- /dev/null +++ b/ledger/onflow/contracts/standard/FungibleTokenMetadataViews.cdc @@ -0,0 +1,185 @@ +import "FungibleToken" +import "MetadataViews" +import "ViewResolver" + +/// This contract implements the metadata standard proposed +/// in FLIP-1087. +/// +/// Ref: https://github.com/onflow/flips/blob/main/application/20220811-fungible-tokens-metadata.md +/// +/// Structs and resources can implement one or more +/// metadata types, called views. Each view type represents +/// a different kind of metadata. +/// +access(all) contract FungibleTokenMetadataViews { + + /// FTView wraps FTDisplay and FTVaultData, and is used to give a complete + /// picture of a Fungible Token. Most Fungible Token contracts should + /// implement this view. + /// + access(all) struct FTView { + access(all) let ftDisplay: FTDisplay? + access(all) let ftVaultData: FTVaultData? + view init( + ftDisplay: FTDisplay?, + ftVaultData: FTVaultData? + ) { + self.ftDisplay = ftDisplay + self.ftVaultData = ftVaultData + } + } + + /// Helper to get a FT view. + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A FTView struct + /// + access(all) fun getFTView(viewResolver: &{ViewResolver.Resolver}): FTView { + let maybeFTView = viewResolver.resolveView(Type()) + if let ftView = maybeFTView { + return ftView as! FTView + } + return FTView( + ftDisplay: self.getFTDisplay(viewResolver), + ftVaultData: self.getFTVaultData(viewResolver) + ) + } + + /// View to expose the information needed to showcase this FT. + /// This can be used by applications to give an overview and + /// graphics of the FT. + /// + access(all) struct FTDisplay { + /// The display name for this token. + /// + /// Example: "Flow" + /// + access(all) let name: String + + /// The abbreviated symbol for this token. + /// + /// Example: "FLOW" + access(all) let symbol: String + + /// A description the provides an overview of this token. + /// + /// Example: "The FLOW token is the native currency of the Flow network." + access(all) let description: String + + /// External link to a URL to view more information about the fungible token. + access(all) let externalURL: MetadataViews.ExternalURL + + /// One or more versions of the fungible token logo. + access(all) let logos: MetadataViews.Medias + + /// Social links to reach the fungible token's social homepages. + /// Possible keys may be "instagram", "twitter", "discord", etc. + access(all) let socials: {String: MetadataViews.ExternalURL} + + view init( + name: String, + symbol: String, + description: String, + externalURL: MetadataViews.ExternalURL, + logos: MetadataViews.Medias, + socials: {String: MetadataViews.ExternalURL} + ) { + self.name = name + self.symbol = symbol + self.description = description + self.externalURL = externalURL + self.logos = logos + self.socials = socials + } + } + + /// Helper to get FTDisplay in a way that will return a typed optional. + /// + /// @param viewResolver: A reference to the resolver resource + /// @return An optional FTDisplay struct + /// + access(all) fun getFTDisplay(_ viewResolver: &{ViewResolver.Resolver}): FTDisplay? { + if let maybeDisplayView = viewResolver.resolveView(Type()) { + if let displayView = maybeDisplayView as? FTDisplay { + return displayView + } + } + return nil + } + + /// View to expose the information needed store and interact with a FT vault. + /// This can be used by applications to setup a FT vault with proper + /// storage and public capabilities. + /// + access(all) struct FTVaultData { + /// Path in storage where this FT vault is recommended to be stored. + access(all) let storagePath: StoragePath + + /// Public path which must be linked to expose the public receiver capability. + access(all) let receiverPath: PublicPath + + /// Public path which must be linked to expose the balance and resolver public capabilities. + access(all) let metadataPath: PublicPath + + /// Type that should be linked at the `receiverPath`. This is a restricted type requiring + /// the `FungibleToken.Receiver` interface. + access(all) let receiverLinkedType: Type + + /// Type that should be linked at the `receiverPath`. This is a restricted type requiring + /// the `ViewResolver.Resolver` interfaces. + access(all) let metadataLinkedType: Type + + /// Function that allows creation of an empty FT vault that is intended + /// to store the funds. + access(all) let createEmptyVault: fun(): @{FungibleToken.Vault} + + view init( + storagePath: StoragePath, + receiverPath: PublicPath, + metadataPath: PublicPath, + receiverLinkedType: Type, + metadataLinkedType: Type, + createEmptyVaultFunction: fun(): @{FungibleToken.Vault} + ) { + pre { + receiverLinkedType.isSubtype(of: Type<&{FungibleToken.Receiver}>()): + "Receiver public type <".concat(receiverLinkedType.identifier) + .concat("> must be a subtype of <").concat(Type<&{FungibleToken.Receiver}>().identifier) + .concat(">.") + metadataLinkedType.isSubtype(of: Type<&{FungibleToken.Vault}>()): + "Metadata linked type <".concat(metadataLinkedType.identifier) + .concat("> must be a subtype of <").concat(Type<&{FungibleToken.Vault}>().identifier) + .concat(">.") + } + self.storagePath = storagePath + self.receiverPath = receiverPath + self.metadataPath = metadataPath + self.receiverLinkedType = receiverLinkedType + self.metadataLinkedType = metadataLinkedType + self.createEmptyVault = createEmptyVaultFunction + } + } + + /// Helper to get FTVaultData in a way that will return a typed Optional. + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A optional FTVaultData struct + /// + access(all) fun getFTVaultData(_ viewResolver: &{ViewResolver.Resolver}): FTVaultData? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? FTVaultData { + return v + } + } + return nil + } + + /// View to expose the total supply of the Vault's token + access(all) struct TotalSupply { + access(all) let supply: UFix64 + + view init(totalSupply: UFix64) { + self.supply = totalSupply + } + } +} diff --git a/ledger/onflow/contracts/standard/FungibleTokenSwitchboard.cdc b/ledger/onflow/contracts/standard/FungibleTokenSwitchboard.cdc new file mode 100644 index 0000000..1e9aa80 --- /dev/null +++ b/ledger/onflow/contracts/standard/FungibleTokenSwitchboard.cdc @@ -0,0 +1,376 @@ +import FungibleToken from "./FungibleToken.cdc" + +/// The contract that allows an account to receive payments in multiple fungible +/// tokens using a single `{FungibleToken.Receiver}` capability. +/// This capability should ideally be stored at the +/// `FungibleTokenSwitchboard.ReceiverPublicPath = /public/GenericFTReceiver` +/// but it can be stored anywhere. +/// +access(all) contract FungibleTokenSwitchboard { + + // Storage and Public Paths + access(all) let StoragePath: StoragePath + access(all) let PublicPath: PublicPath + access(all) let ReceiverPublicPath: PublicPath + + access(all) entitlement Owner + + /// The event that is emitted when a new vault capability is added to a + /// switchboard resource. + /// + access(all) event VaultCapabilityAdded(type: Type, switchboardOwner: Address?, + capabilityOwner: Address?) + + /// The event that is emitted when a vault capability is removed from a + /// switchboard resource. + /// + access(all) event VaultCapabilityRemoved(type: Type, switchboardOwner: Address?, + capabilityOwner: Address?) + + /// The event that is emitted when a deposit can not be completed. + /// + access(all) event NotCompletedDeposit(type: Type, amount: UFix64, + switchboardOwner: Address?) + + /// The interface that enforces the method to allow anyone to check on the + /// available capabilities of a switchboard resource and also exposes the + /// deposit methods to deposit funds on it. + /// + access(all) resource interface SwitchboardPublic { + access(all) view fun getVaultTypesWithAddress(): {Type: Address} + access(all) view fun getSupportedVaultTypes(): {Type: Bool} + access(all) view fun isSupportedVaultType(type: Type): Bool + access(all) fun deposit(from: @{FungibleToken.Vault}) + access(all) fun safeDeposit(from: @{FungibleToken.Vault}): @{FungibleToken.Vault}? + access(all) view fun safeBorrowByType(type: Type): &{FungibleToken.Receiver}? + } + + /// The resource that stores the multiple fungible token receiver + /// capabilities, allowing the owner to add and remove them and anyone to + /// deposit any fungible token among the available types. + /// + access(all) resource Switchboard: FungibleToken.Receiver, SwitchboardPublic { + + /// Dictionary holding the fungible token receiver capabilities, + /// indexed by the fungible token vault type. + /// + access(contract) var receiverCapabilities: {Type: Capability<&{FungibleToken.Receiver}>} + + /// Adds a new fungible token receiver capability to the switchboard + /// resource. + /// + /// @param capability: The capability to expose a certain fungible + /// token vault deposit function through `{FungibleToken.Receiver}` that + /// will be added to the switchboard. + /// + access(Owner) fun addNewVault(capability: Capability<&{FungibleToken.Receiver}>) { + // Borrow a reference to the vault pointed to by the capability we + // want to store inside the switchboard + let vaultRef = capability.borrow() + ?? panic("FungibleTokenSwitchboard.Switchboard.addNewVault: Cannot borrow reference to vault from capability! " + .concat("Make sure that the capability path points to a Vault that has been properly initialized. ")) + + // Check if there is a previous capability for this token + if (self.receiverCapabilities[vaultRef.getType()] == nil) { + // use the vault reference type as key for storing the + // capability and then + self.receiverCapabilities[vaultRef.getType()] = capability + // emit the event that indicates that a new capability has been + // added + emit VaultCapabilityAdded(type: vaultRef.getType(), + switchboardOwner: self.owner?.address, + capabilityOwner: capability.address) + } else { + // If there was already a capability for that token, panic + panic("FungibleTokenSwitchboard.Switchboard.addNewVault: Cannot add new Vault capability! " + .concat("There is already a vault in the Switchboard for this type <") + .concat(vaultRef.getType().identifier).concat(">.")) + } + } + + /// Adds a number of new fungible token receiver capabilities by using + /// the paths where they are stored. + /// + /// @param paths: The paths where the public capabilities are stored. + /// @param address: The address of the owner of the capabilities. + /// + access(Owner) fun addNewVaultsByPath(paths: [PublicPath], address: Address) { + // Get the account where the public capabilities are stored + let owner = getAccount(address) + // For each path, get the saved capability and store it + // into the switchboard's receiver capabilities dictionary + for path in paths { + let capability = owner.capabilities.get<&{FungibleToken.Receiver}>(path) + // Borrow a reference to the vault pointed to by the capability + // we want to store inside the switchboard + // If the vault was borrowed successfully... + if let vaultRef = capability.borrow() { + // ...and if there is no previous capability added for that token + if (self.receiverCapabilities[vaultRef!.getType()] == nil) { + // Use the vault reference type as key for storing the + // capability + self.receiverCapabilities[vaultRef!.getType()] = capability + // and emit the event that indicates that a new + // capability has been added + emit VaultCapabilityAdded(type: vaultRef.getType(), + switchboardOwner: self.owner?.address, + capabilityOwner: address, + ) + } + } + } + } + + /// Adds a new fungible token receiver capability to the switchboard + /// resource specifying which `Type` of `@{FungibleToken.Vault}` can be + /// deposited to it. Use it to include in your switchboard "wrapper" + /// receivers such as a `@TokenForwarding.Forwarder`. It can also be + /// used to overwrite the type attached to a certain capability without + /// having to remove that capability first. + /// + /// @param capability: The capability to expose a certain fungible + /// token vault deposit function through `{FungibleToken.Receiver}` that + /// will be added to the switchboard. + /// + /// @param type: The type of fungible token that can be deposited to that + /// capability, rather than the `Type` from the reference borrowed from + /// said capability + /// + access(Owner) fun addNewVaultWrapper(capability: Capability<&{FungibleToken.Receiver}>, + type: Type) { + // Check if the capability is working + assert ( + capability.check(), + message: + "FungibleTokenSwitchboard.Switchboard.addNewVaultWrapper: Cannot borrow reference to a vault from the provided capability! " + .concat("Make sure that the capability path points to a Vault that has been properly initialized.") + ) + // Use the type parameter as key for the capability + self.receiverCapabilities[type] = capability + // emit the event that indicates that a new capability has been + // added + emit VaultCapabilityAdded( + type: type, + switchboardOwner: self.owner?.address, + capabilityOwner: capability.address, + ) + } + + /// Adds zero or more new fungible token receiver capabilities to the + /// switchboard resource specifying which `Type`s of `@{FungibleToken.Vault}`s + /// can be deposited to it. Use it to include in your switchboard "wrapper" + /// receivers such as a `@TokenForwarding.Forwarder`. It can also be + /// used to overwrite the types attached to certain capabilities without + /// having to remove those capabilities first. + /// + /// @param paths: The paths where the public capabilities are stored. + /// @param types: The types of the fungible token to be deposited on each path. + /// @param address: The address of the owner of the capabilities. + /// + access(Owner) fun addNewVaultWrappersByPath(paths: [PublicPath], types: [Type], + address: Address) { + // Get the account where the public capabilities are stored + let owner = getAccount(address) + // For each path, get the saved capability and store it + // into the switchboard's receiver capabilities dictionary + for i, path in paths { + let capability = owner.capabilities.get<&{FungibleToken.Receiver}>(path) + // Borrow a reference to the vault pointed to by the capability + // we want to store inside the switchboard + // If the vault was borrowed successfully... + if let vaultRef = capability.borrow() { + // Use the vault reference type as key for storing the capability + self.receiverCapabilities[types[i]] = capability + // and emit the event that indicates that a new capability has been added + emit VaultCapabilityAdded( + type: types[i], + switchboardOwner: self.owner?.address, + capabilityOwner: address, + ) + } + } + } + + /// Removes a fungible token receiver capability from the switchboard + /// resource. + /// + /// @param capability: The capability to a fungible token vault to be + /// removed from the switchboard. + /// + access(Owner) fun removeVault(capability: Capability<&{FungibleToken.Receiver}>) { + // Borrow a reference to the vault pointed to by the capability we + // want to remove from the switchboard + let vaultRef = capability.borrow() + ?? panic ("FungibleTokenSwitchboard.Switchboard.addNewVaultWrapper: Cannot borrow reference to a vault from the provided capability! " + .concat("Make sure that the capability path points to a Vault that has been properly initialized.")) + + // Use the vault reference to find the capability to remove + self.receiverCapabilities.remove(key: vaultRef.getType()) + // Emit the event that indicates that a new capability has been + // removed + emit VaultCapabilityRemoved( + type: vaultRef.getType(), + switchboardOwner: self.owner?.address, + capabilityOwner: capability.address, + ) + } + + /// Takes a fungible token vault and routes it to the proper fungible + /// token receiver capability for depositing it. + /// + /// @param from: The deposited fungible token vault resource. + /// + access(all) fun deposit(from: @{FungibleToken.Vault}) { + // Get the capability from the ones stored at the switchboard + let depositedVaultCapability = self.receiverCapabilities[from.getType()] + ?? panic ("FungibleTokenSwitchboard.Switchboard.deposit: Cannot deposit Vault! " + .concat("The deposited vault of type <").concat(from.getType().identifier) + .concat("> is not available on this Fungible Token switchboard. ") + .concat("The recipient needs to initialize their account and switchboard to hold and receive the deposited vault type.")) + + // Borrow the reference to the desired vault + let vaultRef = depositedVaultCapability.borrow() + ?? panic ("FungibleTokenSwitchboard.Switchboard.deposit: Cannot borrow reference to a vault " + .concat("from the type of the deposited Vault <").concat(from.getType().identifier) + .concat(">. Make sure that the capability path points to a Vault that has been properly initialized.")) + + vaultRef.deposit(from: <-from) + } + + /// Takes a fungible token vault and tries to route it to the proper + /// fungible token receiver capability for depositing the funds, + /// avoiding panicking if the vault is not available. + /// + /// @param vaultType: The type of the ft vault that wants to be + /// deposited. + /// + /// @return The deposited fungible token vault resource, without the + /// funds if the deposit was successful, or still containing the funds + /// if the reference to the needed vault was not found. + /// + access(all) fun safeDeposit(from: @{FungibleToken.Vault}): @{FungibleToken.Vault}? { + // Try to get the proper vault capability from the switchboard + // If the desired vault is present on the switchboard... + if let depositedVaultCapability = self.receiverCapabilities[from.getType()] { + // We try to borrow a reference to the vault from the capability + // If we can borrow a reference to the vault... + if let vaultRef = depositedVaultCapability.borrow() { + // We deposit the funds on said vault + vaultRef.deposit(from: <-from.withdraw(amount: from.balance)) + } + } + // if deposit failed for some reason + if from.balance > 0.0 { + emit NotCompletedDeposit( + type: from.getType(), + amount: from.balance, + switchboardOwner: self.owner?.address, + ) + return <-from + } + destroy from + return nil + } + + /// Checks that the capability tied to a type is valid + /// + /// @param vaultType: The type of the ft vault whose capability needs to be checked + /// + /// @return a boolean marking the capability for a type as valid or not + access(all) view fun checkReceiverByType(type: Type): Bool { + if self.receiverCapabilities[type] == nil { + return false + } + + return self.receiverCapabilities[type]!.check() + } + + /// Gets the receiver assigned to a provided vault type. + /// This is necessary because without it, it is not possible to look under the hood and see if a capability + /// is of an expected type or not. This helps guard against infinitely chained TokenForwarding or other invalid + /// malicious kinds of updates that could prevent listings from being made that are valid on storefronts. + /// + /// @param vaultType: The type of the ft vault whose capability needs to be checked + /// + /// @return an optional receiver capability for consumers of the switchboard to check/validate on their own + access(all) view fun safeBorrowByType(type: Type): &{FungibleToken.Receiver}? { + if !self.checkReceiverByType(type: type) { + return nil + } + + return self.receiverCapabilities[type]!.borrow() + } + + /// A getter function to know which tokens a certain switchboard + /// resource is prepared to receive along with the address where + /// those tokens will be deposited. + /// + /// @return A dictionary mapping the `{FungibleToken.Receiver}` + /// type to the receiver owner's address + /// + access(all) view fun getVaultTypesWithAddress(): {Type: Address} { + let effectiveTypesWithAddress: {Type: Address} = {} + // Check if each capability is live + for vaultType in self.receiverCapabilities.keys { + if self.receiverCapabilities[vaultType]!.check() { + // and attach it to the owner's address + effectiveTypesWithAddress[vaultType] = self.receiverCapabilities[vaultType]!.address + } + } + return effectiveTypesWithAddress + } + + /// A getter function that returns the token types supported by this resource, + /// which can be deposited using the 'deposit' function. + /// + /// @return Dictionary of FT types that can be deposited. + access(all) view fun getSupportedVaultTypes(): {Type: Bool} { + let supportedVaults: {Type: Bool} = {} + for receiverType in self.receiverCapabilities.keys { + if self.receiverCapabilities[receiverType]!.check() { + if receiverType.isSubtype(of: Type<@{FungibleToken.Vault}>()) { + supportedVaults[receiverType] = true + } + if receiverType.isSubtype(of: Type<@{FungibleToken.Receiver}>()) { + let receiverRef = self.receiverCapabilities[receiverType]!.borrow()! + let subReceiverSupportedTypes = receiverRef.getSupportedVaultTypes() + for subReceiverType in subReceiverSupportedTypes.keys { + if subReceiverType.isSubtype(of: Type<@{FungibleToken.Vault}>()) { + supportedVaults[subReceiverType] = true + } + } + } + } + } + return supportedVaults + } + + /// Returns whether or not the given type is accepted by the Receiver + /// A vault that can accept any type should just return true by default + access(all) view fun isSupportedVaultType(type: Type): Bool { + let supportedVaults = self.getSupportedVaultTypes() + if let supported = supportedVaults[type] { + return supported + } else { return false } + } + + init() { + // Initialize the capabilities dictionary + self.receiverCapabilities = {} + } + + } + + /// Function that allows to create a new blank switchboard. A user must call + /// this function and store the returned resource in their storage. + /// + access(all) fun createSwitchboard(): @Switchboard { + return <-create Switchboard() + } + + init() { + self.StoragePath = /storage/fungibleTokenSwitchboard + self.PublicPath = /public/fungibleTokenSwitchboardPublic + self.ReceiverPublicPath = /public/GenericFTReceiver + } +} diff --git a/ledger/onflow/contracts/standard/MetadataViews.cdc b/ledger/onflow/contracts/standard/MetadataViews.cdc new file mode 100644 index 0000000..8ce2303 --- /dev/null +++ b/ledger/onflow/contracts/standard/MetadataViews.cdc @@ -0,0 +1,805 @@ +import FungibleToken from "./FungibleToken.cdc" +import NonFungibleToken from "./NonFungibleToken.cdc" +import ViewResolver from "./ViewResolver.cdc" + +/// This contract implements the metadata standard proposed +/// in FLIP-0636. +/// +/// Ref: https://github.com/onflow/flips/blob/main/application/20210916-nft-metadata.md +/// +/// Structs and resources can implement one or more +/// metadata types, called views. Each view type represents +/// a different kind of metadata, such as a creator biography +/// or a JPEG image file. +/// +access(all) contract MetadataViews { + + /// Display is a basic view that includes the name, description and + /// thumbnail for an object. Most objects should implement this view. + /// + access(all) struct Display { + + /// The name of the object. + /// + /// This field will be displayed in lists and therefore should + /// be short an concise. + /// + access(all) let name: String + + /// A written description of the object. + /// + /// This field will be displayed in a detailed view of the object, + /// so can be more verbose (e.g. a paragraph instead of a single line). + /// + access(all) let description: String + + /// A small thumbnail representation of the object. + /// + /// This field should be a web-friendly file (i.e JPEG, PNG) + /// that can be displayed in lists, link previews, etc. + /// + access(all) let thumbnail: {File} + + view init( + name: String, + description: String, + thumbnail: {File} + ) { + self.name = name + self.description = description + self.thumbnail = thumbnail + } + } + + /// Helper to get Display in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return An optional Display struct + /// + access(all) fun getDisplay(_ viewResolver: &{ViewResolver.Resolver}) : Display? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? Display { + return v + } + } + return nil + } + + /// Generic interface that represents a file stored on or off chain. Files + /// can be used to references images, videos and other media. + /// + access(all) struct interface File { + access(all) view fun uri(): String + } + + /// View to expose a file that is accessible at an HTTP (or HTTPS) URL. + /// + access(all) struct HTTPFile: File { + access(all) let url: String + + view init(url: String) { + self.url = url + } + + access(all) view fun uri(): String { + return self.url + } + } + + /// View to expose a file stored on IPFS. + /// IPFS images are referenced by their content identifier (CID) + /// rather than a direct URI. A client application can use this CID + /// to find and load the image via an IPFS gateway. + /// + access(all) struct IPFSFile: File { + + /// CID is the content identifier for this IPFS file. + /// + /// Ref: https://docs.ipfs.io/concepts/content-addressing/ + /// + access(all) let cid: String + + /// Path is an optional path to the file resource in an IPFS directory. + /// + /// This field is only needed if the file is inside a directory. + /// + /// Ref: https://docs.ipfs.io/concepts/file-systems/ + /// + access(all) let path: String? + + view init(cid: String, path: String?) { + self.cid = cid + self.path = path + } + + /// This function returns the IPFS native URL for this file. + /// Ref: https://docs.ipfs.io/how-to/address-ipfs-on-web/#native-urls + /// + /// @return The string containing the file uri + /// + access(all) view fun uri(): String { + if let path = self.path { + return "ipfs://".concat(self.cid).concat("/").concat(path) + } + + return "ipfs://".concat(self.cid) + } + } + + /// A struct to represent a generic URI. May be used to represent the URI of + /// the NFT where the type of URI is not able to be determined (i.e. HTTP, + /// IPFS, etc.) + /// + access(all) struct URI: File { + /// The base URI prefix, if any. Not needed for all URIs, but helpful + /// for some use cases For example, updating a whole NFT collection's + /// image host easily + /// + access(all) let baseURI: String? + /// The URI string value + /// NOTE: this is set on init as a concatenation of the baseURI and the + /// value if baseURI != nil + /// + access(self) let value: String + + access(all) view fun uri(): String { + return self.value + } + + init(baseURI: String?, value: String) { + self.baseURI = baseURI + self.value = baseURI != nil ? baseURI!.concat(value) : value + } + } + + access(all) struct Media { + + /// File for the media + /// + access(all) let file: {File} + + /// media-type comes on the form of type/subtype as described here + /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types + /// + access(all) let mediaType: String + + view init(file: {File}, mediaType: String) { + self.file=file + self.mediaType=mediaType + } + } + + /// Wrapper view for multiple media views + /// + access(all) struct Medias { + + /// An arbitrary-sized list for any number of Media items + access(all) let items: [Media] + + view init(_ items: [Media]) { + self.items = items + } + } + + /// Helper to get Medias in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A optional Medias struct + /// + access(all) fun getMedias(_ viewResolver: &{ViewResolver.Resolver}) : Medias? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? Medias { + return v + } + } + return nil + } + + /// View to represent a license according to https://spdx.org/licenses/ + /// This view can be used if the content of an NFT is licensed. + /// + access(all) struct License { + access(all) let spdxIdentifier: String + + view init(_ identifier: String) { + self.spdxIdentifier = identifier + } + } + + /// Helper to get License in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return An optional License struct + /// + access(all) fun getLicense(_ viewResolver: &{ViewResolver.Resolver}) : License? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? License { + return v + } + } + return nil + } + + /// View to expose a URL to this item on an external site. + /// This can be used by applications like .find and Blocto to direct users + /// to the original link for an NFT or a project page that describes the NFT collection. + /// eg https://www.my-nft-project.com/overview-of-nft-collection + /// + access(all) struct ExternalURL { + access(all) let url: String + + view init(_ url: String) { + self.url=url + } + } + + /// Helper to get ExternalURL in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return An optional ExternalURL struct + /// + access(all) fun getExternalURL(_ viewResolver: &{ViewResolver.Resolver}) : ExternalURL? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? ExternalURL { + return v + } + } + return nil + } + + /// View that defines the composable royalty standard that gives marketplaces a + /// unified interface to support NFT royalties. + /// + access(all) struct Royalty { + + /// Generic FungibleToken Receiver for the beneficiary of the royalty + /// Can get the concrete type of the receiver with receiver.getType() + /// Recommendation - Users should create a new link for a FlowToken + /// receiver for this using `getRoyaltyReceiverPublicPath()`, and not + /// use the default FlowToken receiver. This will allow users to update + /// the capability in the future to use a more generic capability + access(all) let receiver: Capability<&{FungibleToken.Receiver}> + + /// Multiplier used to calculate the amount of sale value transferred to + /// royalty receiver. Note - It should be between 0.0 and 1.0 + /// Ex - If the sale value is x and multiplier is 0.56 then the royalty + /// value would be 0.56 * x. + /// Generally percentage get represented in terms of basis points + /// in solidity based smart contracts while cadence offers `UFix64` + /// that already supports the basis points use case because its + /// operations are entirely deterministic integer operations and support + /// up to 8 points of precision. + access(all) let cut: UFix64 + + /// Optional description: This can be the cause of paying the royalty, + /// the relationship between the `wallet` and the NFT, or anything else + /// that the owner might want to specify. + access(all) let description: String + + view init(receiver: Capability<&{FungibleToken.Receiver}>, cut: UFix64, description: String) { + pre { + cut >= 0.0 && cut <= 1.0 : + "MetadataViews.Royalty.init: Cannot initialize the Royalty Metadata View! " + .concat("The provided royalty cut value of ").concat(cut.toString()) + .concat(" is invalid. ") + .concat("It should be within the valid range between 0 and 1. i.e [0,1]") + } + self.receiver = receiver + self.cut = cut + self.description = description + } + } + + /// Wrapper view for multiple Royalty views. + /// Marketplaces can query this `Royalties` struct from NFTs + /// and are expected to pay royalties based on these specifications. + /// + access(all) struct Royalties { + + /// Array that tracks the individual royalties + access(self) let cutInfos: [Royalty] + + access(all) view init(_ cutInfos: [Royalty]) { + // Validate that sum of all cut multipliers should not be greater than 1.0 + var totalCut = 0.0 + for royalty in cutInfos { + totalCut = totalCut + royalty.cut + } + assert( + totalCut <= 1.0, + message: + "MetadataViews.Royalties.init: Cannot initialize Royalties Metadata View! " + .concat(" The sum of cutInfos multipliers is ") + .concat(totalCut.toString()) + .concat(" but it should not be greater than 1.0") + ) + // Assign the cutInfos + self.cutInfos = cutInfos + } + + /// Return the cutInfos list + /// + /// @return An array containing all the royalties structs + /// + access(all) view fun getRoyalties(): [Royalty] { + return self.cutInfos + } + } + + /// Helper to get Royalties in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A optional Royalties struct + /// + access(all) fun getRoyalties(_ viewResolver: &{ViewResolver.Resolver}) : Royalties? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? Royalties { + return v + } + } + return nil + } + + /// Get the path that should be used for receiving royalties + /// This is a path that will eventually be used for a generic switchboard receiver, + /// hence the name but will only be used for royalties for now. + /// + /// @return The PublicPath for the generic FT receiver + /// + access(all) view fun getRoyaltyReceiverPublicPath(): PublicPath { + return /public/GenericFTReceiver + } + + /// View to represent a single field of metadata on an NFT. + /// This is used to get traits of individual key/value pairs along with some + /// contextualized data about the trait + /// + access(all) struct Trait { + // The name of the trait. Like Background, Eyes, Hair, etc. + access(all) let name: String + + // The underlying value of the trait, the rest of the fields of a trait provide context to the value. + access(all) let value: AnyStruct + + // displayType is used to show some context about what this name and value represent + // for instance, you could set value to a unix timestamp, and specify displayType as "Date" to tell + // platforms to consume this trait as a date and not a number + access(all) let displayType: String? + + // Rarity can also be used directly on an attribute. + // + // This is optional because not all attributes need to contribute to the NFT's rarity. + access(all) let rarity: Rarity? + + view init(name: String, value: AnyStruct, displayType: String?, rarity: Rarity?) { + self.name = name + self.value = value + self.displayType = displayType + self.rarity = rarity + } + } + + /// Wrapper view to return all the traits on an NFT. + /// This is used to return traits as individual key/value pairs along with + /// some contextualized data about each trait. + access(all) struct Traits { + access(all) let traits: [Trait] + + view init(_ traits: [Trait]) { + self.traits = traits + } + + /// Adds a single Trait to the Traits view + /// + /// @param Trait: The trait struct to be added + /// + access(all) fun addTrait(_ t: Trait) { + self.traits.append(t) + } + } + + /// Helper to get Traits view in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A optional Traits struct + /// + access(all) fun getTraits(_ viewResolver: &{ViewResolver.Resolver}) : Traits? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? Traits { + return v + } + } + return nil + } + + /// Helper function to easily convert a dictionary to traits. For NFT + /// collections that do not need either of the optional values of a Trait, + /// this method should suffice to give them an array of valid traits. + /// + /// @param dict: The dictionary to be converted to Traits + /// @param excludedNames: An optional String array specifying the `dict` + /// keys that are not wanted to become `Traits` + /// @return The generated Traits view + /// + access(all) fun dictToTraits(dict: {String: AnyStruct}, excludedNames: [String]?): Traits { + // Collection owners might not want all the fields in their metadata included. + // They might want to handle some specially, or they might just not want them included at all. + if excludedNames != nil { + for k in excludedNames! { + dict.remove(key: k) + } + } + + let traits: [Trait] = [] + for k in dict.keys { + let trait = Trait(name: k, value: dict[k]!, displayType: nil, rarity: nil) + traits.append(trait) + } + + return Traits(traits) + } + + /// Optional view for collections that issue multiple objects + /// with the same or similar metadata, for example an X of 100 set. This + /// information is useful for wallets and marketplaces. + /// An NFT might be part of multiple editions, which is why the edition + /// information is returned as an arbitrary sized array + /// + access(all) struct Edition { + + /// The name of the edition + /// For example, this could be Set, Play, Series, + /// or any other way a project could classify its editions + access(all) let name: String? + + /// The edition number of the object. + /// For an "24 of 100 (#24/100)" item, the number is 24. + access(all) let number: UInt64 + + /// The max edition number of this type of objects. + /// This field should only be provided for limited-editioned objects. + /// For an "24 of 100 (#24/100)" item, max is 100. + /// For an item with unlimited edition, max should be set to nil. + /// + access(all) let max: UInt64? + + view init(name: String?, number: UInt64, max: UInt64?) { + if max != nil { + assert( + number <= max!, + message: + "MetadataViews.Edition.init: Cannot intialize the Edition Metadata View! " + .concat("The provided edition number of ") + .concat(number.toString()) + .concat(" cannot be greater than the max edition number of ") + .concat(max!.toString()) + .concat(".") + ) + } + self.name = name + self.number = number + self.max = max + } + } + + /// Wrapper view for multiple Edition views + /// + access(all) struct Editions { + + /// An arbitrary-sized list for any number of editions + /// that the NFT might be a part of + access(all) let infoList: [Edition] + + view init(_ infoList: [Edition]) { + self.infoList = infoList + } + } + + /// Helper to get Editions in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return An optional Editions struct + /// + access(all) fun getEditions(_ viewResolver: &{ViewResolver.Resolver}) : Editions? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? Editions { + return v + } + } + return nil + } + + /// View representing a project-defined serial number for a specific NFT + /// Projects have different definitions for what a serial number should be + /// Some may use the NFTs regular ID and some may use a different + /// classification system. The serial number is expected to be unique among + /// other NFTs within that project + /// + access(all) struct Serial { + access(all) let number: UInt64 + + view init(_ number: UInt64) { + self.number = number + } + } + + /// Helper to get Serial in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return An optional Serial struct + /// + access(all) fun getSerial(_ viewResolver: &{ViewResolver.Resolver}) : Serial? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? Serial { + return v + } + } + return nil + } + + /// View to expose rarity information for a single rarity + /// Note that a rarity needs to have either score or description but it can + /// have both + /// + access(all) struct Rarity { + /// The score of the rarity as a number + access(all) let score: UFix64? + + /// The maximum value of score + access(all) let max: UFix64? + + /// The description of the rarity as a string. + /// + /// This could be Legendary, Epic, Rare, Uncommon, Common or any other string value + access(all) let description: String? + + view init(score: UFix64?, max: UFix64?, description: String?) { + if score == nil && description == nil { + panic("MetadataViews.Rarity.init: Cannot initialize the Rarity Metadata View! " + .concat("The provided score and description are both `nil`. ") + .concat(" A Rarity needs to set score, description, or both")) + } + + self.score = score + self.max = max + self.description = description + } + } + + /// Helper to get Rarity view in a typesafe way + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A optional Rarity struct + /// + access(all) fun getRarity(_ viewResolver: &{ViewResolver.Resolver}) : Rarity? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? Rarity { + return v + } + } + return nil + } + + /// NFTView wraps all Core views along `id` and `uuid` fields, and is used + /// to give a complete picture of an NFT. Most NFTs should implement this + /// view. + /// + access(all) struct NFTView { + access(all) let id: UInt64 + access(all) let uuid: UInt64 + access(all) let display: MetadataViews.Display? + access(all) let externalURL: MetadataViews.ExternalURL? + access(all) let collectionData: NFTCollectionData? + access(all) let collectionDisplay: NFTCollectionDisplay? + access(all) let royalties: Royalties? + access(all) let traits: Traits? + + view init( + id : UInt64, + uuid : UInt64, + display : MetadataViews.Display?, + externalURL : MetadataViews.ExternalURL?, + collectionData : NFTCollectionData?, + collectionDisplay : NFTCollectionDisplay?, + royalties : Royalties?, + traits: Traits? + ) { + self.id = id + self.uuid = uuid + self.display = display + self.externalURL = externalURL + self.collectionData = collectionData + self.collectionDisplay = collectionDisplay + self.royalties = royalties + self.traits = traits + } + } + + /// Helper to get an NFT view + /// + /// @param id: The NFT id + /// @param viewResolver: A reference to the resolver resource + /// @return A NFTView struct + /// + access(all) fun getNFTView(id: UInt64, viewResolver: &{ViewResolver.Resolver}) : NFTView { + let nftView = viewResolver.resolveView(Type()) + if nftView != nil { + return nftView! as! NFTView + } + + return NFTView( + id : id, + uuid: viewResolver.uuid, + display: MetadataViews.getDisplay(viewResolver), + externalURL : MetadataViews.getExternalURL(viewResolver), + collectionData : self.getNFTCollectionData(viewResolver), + collectionDisplay : self.getNFTCollectionDisplay(viewResolver), + royalties : self.getRoyalties(viewResolver), + traits : self.getTraits(viewResolver) + ) + } + + /// View to expose the information needed store and retrieve an NFT. + /// This can be used by applications to setup a NFT collection with proper + /// storage and public capabilities. + /// + access(all) struct NFTCollectionData { + /// Path in storage where this NFT is recommended to be stored. + access(all) let storagePath: StoragePath + + /// Public path which must be linked to expose public capabilities of this NFT + /// including standard NFT interfaces and metadataviews interfaces + access(all) let publicPath: PublicPath + + /// The concrete type of the collection that is exposed to the public + /// now that entitlements exist, it no longer needs to be restricted to a specific interface + access(all) let publicCollection: Type + + /// Type that should be linked at the aforementioned public path + access(all) let publicLinkedType: Type + + /// Function that allows creation of an empty NFT collection that is intended to store + /// this NFT. + access(all) let createEmptyCollection: fun(): @{NonFungibleToken.Collection} + + view init( + storagePath: StoragePath, + publicPath: PublicPath, + publicCollection: Type, + publicLinkedType: Type, + createEmptyCollectionFunction: fun(): @{NonFungibleToken.Collection} + ) { + pre { + publicLinkedType.isSubtype(of: Type<&{NonFungibleToken.Collection}>()): + "MetadataViews.NFTCollectionData.init: Cannot initialize the NFTCollectionData Metadata View! " + .concat("The Public linked type <") + .concat(publicLinkedType.identifier) + .concat("> is incorrect. It must be a subtype of the NonFungibleToken.Collection interface.") + } + self.storagePath=storagePath + self.publicPath=publicPath + self.publicCollection=publicCollection + self.publicLinkedType=publicLinkedType + self.createEmptyCollection=createEmptyCollectionFunction + } + } + + /// Helper to get NFTCollectionData in a way that will return an typed Optional + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A optional NFTCollectionData struct + /// + access(all) fun getNFTCollectionData(_ viewResolver: &{ViewResolver.Resolver}) : NFTCollectionData? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? NFTCollectionData { + return v + } + } + return nil + } + + /// View to expose the information needed to showcase this NFT's + /// collection. This can be used by applications to give an overview and + /// graphics of the NFT collection this NFT belongs to. + /// + access(all) struct NFTCollectionDisplay { + // Name that should be used when displaying this NFT collection. + access(all) let name: String + + // Description that should be used to give an overview of this collection. + access(all) let description: String + + // External link to a URL to view more information about this collection. + access(all) let externalURL: MetadataViews.ExternalURL + + // Square-sized image to represent this collection. + access(all) let squareImage: MetadataViews.Media + + // Banner-sized image for this collection, recommended to have a size near 1400x350. + access(all) let bannerImage: MetadataViews.Media + + // Social links to reach this collection's social homepages. + // Possible keys may be "instagram", "twitter", "discord", etc. + access(all) let socials: {String: MetadataViews.ExternalURL} + + view init( + name: String, + description: String, + externalURL: MetadataViews.ExternalURL, + squareImage: MetadataViews.Media, + bannerImage: MetadataViews.Media, + socials: {String: MetadataViews.ExternalURL} + ) { + self.name = name + self.description = description + self.externalURL = externalURL + self.squareImage = squareImage + self.bannerImage = bannerImage + self.socials = socials + } + } + + /// Helper to get NFTCollectionDisplay in a way that will return a typed + /// Optional + /// + /// @param viewResolver: A reference to the resolver resource + /// @return A optional NFTCollection struct + /// + access(all) fun getNFTCollectionDisplay(_ viewResolver: &{ViewResolver.Resolver}) : NFTCollectionDisplay? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? NFTCollectionDisplay { + return v + } + } + return nil + } + /// This view may be used by Cadence-native projects to define their + /// contract- and token-level metadata according to EVM-compatible formats. + /// Several ERC standards (e.g. ERC20, ERC721, etc.) expose name and symbol + /// values to define assets as well as contract- & token-level metadata view + /// `tokenURI(uint256)` and `contractURI()` methods. This view enables + /// Cadence projects to define in their own contracts how they would like + /// their metadata to be defined when bridged to EVM. + /// + access(all) struct EVMBridgedMetadata { + + /// The name of the asset + /// + access(all) let name: String + + /// The symbol of the asset + /// + access(all) let symbol: String + + /// The URI of the asset - this can either be contract-level or + /// token-level URI depending on where the metadata is resolved. It + /// is recommended to reference EVM metadata standards for how to best + /// prepare your view's formatted value. + /// + /// For example, while you may choose to take advantage of onchain + /// metadata, as is the case for most Cadence NFTs, you may also choose + /// to represent your asset's metadata in IPFS and assign this value as + /// an IPFSFile struct pointing to that IPFS file. Alternatively, you + /// may serialize your NFT's metadata and assign it as a JSON string + /// data URL representating the NFT's onchain metadata at the time this + /// view is resolved. + /// + access(all) let uri: {File} + + init(name: String, symbol: String, uri: {File}) { + self.name = name + self.symbol = symbol + self.uri = uri + } + } + + access(all) fun getEVMBridgedMetadata(_ viewResolver: &{ViewResolver.Resolver}) : EVMBridgedMetadata? { + if let view = viewResolver.resolveView(Type()) { + if let v = view as? EVMBridgedMetadata { + return v + } + } + return nil + } + +} diff --git a/ledger/onflow/contracts/standard/NonFungibleToken.cdc b/ledger/onflow/contracts/standard/NonFungibleToken.cdc new file mode 100644 index 0000000..2c6a8b5 --- /dev/null +++ b/ledger/onflow/contracts/standard/NonFungibleToken.cdc @@ -0,0 +1,293 @@ +/** + +## The Flow Non-Fungible Token standard + +## `NonFungibleToken` contract + +The interface that all Non-Fungible Token contracts should conform to. +If a user wants to deploy a new NFT contract, their contract should implement +The types defined here + +/// Contributors (please add to this list if you contribute!): +/// - Joshua Hannan - https://github.com/joshuahannan +/// - Bastian Müller - https://twitter.com/turbolent +/// - Dete Shirley - https://twitter.com/dete73 +/// - Bjarte Karlsen - https://twitter.com/0xBjartek +/// - Austin Kline - https://twitter.com/austin_flowty +/// - Giovanni Sanchez - https://twitter.com/gio_incognito +/// - Deniz Edincik - https://twitter.com/bluesign +/// +/// Repo reference: https://github.com/onflow/flow-nft + +## `NFT` resource interface + +The core resource type that represents an NFT in the smart contract. + +## `Collection` Resource interface + +The resource that stores a user's NFT collection. +It includes a few functions to allow the owner to easily +move tokens in and out of the collection. + +## `Provider` and `Receiver` resource interfaces + +These interfaces declare functions with some pre and post conditions +that require the Collection to follow certain naming and behavior standards. + +They are separate because it gives developers the ability to define functions +that can use any type that implements these interfaces + +By using resources and interfaces, users of NFT smart contracts can send +and receive tokens peer-to-peer, without having to interact with a central ledger +smart contract. + +To send an NFT to another user, a user would simply withdraw the NFT +from their Collection, then call the deposit function on another user's +Collection to complete the transfer. + +*/ + +import "ViewResolver" + +/// The main NFT contract interface. Other NFT contracts will import +/// and implement this interface as well the interfaces defined in this interface +/// +access(all) contract interface NonFungibleToken: ViewResolver { + + /// An entitlement for allowing the withdrawal of tokens from a Vault + access(all) entitlement Withdraw + + /// An entitlement for allowing updates and update events for an NFT + access(all) entitlement Update + + /// Event that contracts should emit when the metadata of an NFT is updated + /// It can only be emitted by calling the `emitNFTUpdated` function + /// with an `Update` entitled reference to the NFT that was updated + /// The entitlement prevents spammers from calling this from other users' collections + /// because only code within a collection or that has special entitled access + /// to the collections methods will be able to get the entitled reference + /// + /// The event makes it so that third-party indexers can monitor the events + /// and query the updated metadata from the owners' collections. + /// + access(all) event Updated(type: String, id: UInt64, uuid: UInt64, owner: Address?) + access(all) view fun emitNFTUpdated(_ nftRef: auth(Update) &{NonFungibleToken.NFT}) + { + emit Updated(type: nftRef.getType().identifier, id: nftRef.id, uuid: nftRef.uuid, owner: nftRef.owner?.address) + } + + + /// Event that is emitted when a token is withdrawn, + /// indicating the type, id, uuid, the owner of the collection that it was withdrawn from, + /// and the UUID of the resource it was withdrawn from, usually a collection. + /// + /// If the collection is not in an account's storage, `from` will be `nil`. + /// + access(all) event Withdrawn(type: String, id: UInt64, uuid: UInt64, from: Address?, providerUUID: UInt64) + + /// Event that emitted when a token is deposited to a collection. + /// Indicates the type, id, uuid, the owner of the collection that it was deposited to, + /// and the UUID of the collection it was deposited to + /// + /// If the collection is not in an account's storage, `from`, will be `nil`. + /// + access(all) event Deposited(type: String, id: UInt64, uuid: UInt64, to: Address?, collectionUUID: UInt64) + + /// Interface that the NFTs must conform to + /// + access(all) resource interface NFT: ViewResolver.Resolver { + + /// unique ID for the NFT + access(all) let id: UInt64 + + /// Event that is emitted automatically every time a resource is destroyed + /// The type information is included in the metadata event so it is not needed as an argument + access(all) event ResourceDestroyed(id: UInt64 = self.id, uuid: UInt64 = self.uuid) + + /// createEmptyCollection creates an empty Collection that is able to store the NFT + /// and returns it to the caller so that they can own NFTs + /// + /// @return A an empty collection that can store this NFT + /// + access(all) fun createEmptyCollection(): @{Collection} { + post { + result.getLength() == 0: + "NonFungibleToken.NFT.createEmptyCollection: Cannot create an empty collection! " + .concat("The created NonFungibleToken Collection has a non-zero length. ") + .concat(" A newly created collection must be empty!") + result.isSupportedNFTType(type: self.getType()): + "NonFungibleToken.NFT.createEmptyCollection: Cannot create an empty collection! " + .concat("The created NonFungibleToken Collection does not support NFTs of type <") + .concat(self.getType().identifier) + .concat(">. The collection must support NFTs of type <") + .concat(self.getType().identifier).concat(">.") + } + } + + /// Gets all the NFTs that this NFT directly owns + /// + /// @return A dictionary of all subNFTS keyed by type + /// + access(all) view fun getAvailableSubNFTS(): {Type: [UInt64]} { + return {} + } + + /// Get a reference to an NFT that this NFT owns + /// Both arguments are optional to allow the NFT to choose + /// how it returns sub NFTs depending on what arguments are provided + /// For example, if `type` has a value, but `id` doesn't, the NFT + /// can choose which NFT of that type to return if there is a "default" + /// If both are `nil`, then NFTs that only store a single NFT can just return + /// that. This helps callers who aren't sure what they are looking for + /// + /// @param type: The Type of the desired NFT + /// @param id: The id of the NFT to borrow + /// + /// @return A structure representing the requested view. + access(all) fun getSubNFT(type: Type, id: UInt64) : &{NonFungibleToken.NFT}? { + return nil + } + } + + /// Interface to mediate withdrawals from a resource, usually a Collection + /// + access(all) resource interface Provider { + + // We emit withdraw events from the provider interface because conficting withdraw + // events aren't as confusing to event listeners as conflicting deposit events + + /// withdraw removes an NFT from the collection and moves it to the caller + /// It does not specify whether the ID is UUID or not + /// + /// @param withdrawID: The id of the NFT to withdraw from the collection + /// @return @{NFT}: The NFT that was withdrawn + /// + access(Withdraw) fun withdraw(withdrawID: UInt64): @{NFT} { + post { + result.id == withdrawID: + "NonFungibleToken.Provider.withdraw: Cannot withdraw NFT! " + .concat("The ID of the withdrawn NFT (") + .concat(result.id.toString()) + .concat(") must be the same as the requested ID (") + .concat(withdrawID.toString()) + .concat(").") + emit Withdrawn(type: result.getType().identifier, id: result.id, uuid: result.uuid, from: self.owner?.address, providerUUID: self.uuid) + } + } + } + + /// Interface to mediate deposits to the Collection + /// + access(all) resource interface Receiver { + + /// deposit takes an NFT as an argument and adds it to the Collection + /// @param token: The NFT to deposit + access(all) fun deposit(token: @{NFT}) + + /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts + /// @return A dictionary of types mapped to booleans indicating if this + /// reciever supports it + access(all) view fun getSupportedNFTTypes(): {Type: Bool} + + /// Returns whether or not the given type is accepted by the collection + /// A collection that can accept any type should just return true by default + /// @param type: An NFT type + /// @return A boolean indicating if this receiver can recieve the desired NFT type + access(all) view fun isSupportedNFTType(type: Type): Bool + } + + /// Kept for backwards-compatibility reasons + access(all) resource interface CollectionPublic { + access(all) fun deposit(token: @{NFT}) + access(all) view fun getLength(): Int + access(all) view fun getIDs(): [UInt64] + access(all) fun forEachID(_ f: fun (UInt64): Bool): Void + access(all) view fun borrowNFT(_ id: UInt64): &{NFT}? + } + + /// Requirement for the concrete resource type in the implementing contract + /// to implement this interface. Since this interface inherits from + /// all the other necessary interfaces, resources that implement it do not + /// also need to include the other interfaces in their conformance lists + /// + access(all) resource interface Collection: Provider, Receiver, CollectionPublic, ViewResolver.ResolverCollection { + + /// Field that contains all the NFTs that the collection owns + access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} + + /// deposit takes a NFT as an argument and stores it in the collection + /// @param token: The NFT to deposit into the collection + access(all) fun deposit(token: @{NonFungibleToken.NFT}) { + pre { + // We emit the deposit event in the `Collection` interface + // because the `Collection` interface is almost always the final destination + // of tokens and deposit emissions from custom receivers could be confusing + // and hard to reconcile to event listeners + emit Deposited(type: token.getType().identifier, id: token.id, uuid: token.uuid, to: self.owner?.address, collectionUUID: self.uuid) + } + } + + /// Gets the amount of NFTs stored in the collection + /// @return An integer indicating the size of the collection + access(all) view fun getLength(): Int { + return self.ownedNFTs.length + } + + /// Allows a given function to iterate through the list + /// of owned NFT IDs in a collection without first + /// having to load the entire list into memory + access(all) fun forEachID(_ f: fun (UInt64): Bool): Void { + self.ownedNFTs.forEachKey(f) + } + + /// Borrows a reference to an NFT stored in the collection + /// If the NFT with the specified ID is not in the collection, + /// the function should return `nil` and not panic. + /// + /// @param id: The desired nft id in the collection to return a referece for. + /// @return An optional reference to the NFT + access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { + post { + (result == nil) || (result?.id == id): + "NonFungibleToken.Collection.borrowNFT: Cannot borrow NFT reference! " + .concat("The ID of the returned reference (") + .concat(result!.id.toString()) + .concat(") does not match the ID that was specified (") + .concat(id.toString()) + .concat(")") + } + } + + /// createEmptyCollection creates an empty Collection of the same type + /// and returns it to the caller + /// @return A an empty collection of the same type + access(all) fun createEmptyCollection(): @{Collection} { + post { + result.getType() == self.getType(): + "NonFungibleToken.Collection.createEmptyCollection: Cannot create empty collection! " + .concat("The created collection type <") + .concat(result.getType().identifier) + .concat("> does not have the same type as the collection that was used to create it <") + .concat(self.getType().identifier) + .concat(">.") + result.getLength() == 0: + "NonFungibleToken.Collection.createEmptyCollection: Cannot create empty collection! " + .concat("The created collection has a non-zero length.") + .concat(" A newly created collection must be empty!") + } + } + } + + /// createEmptyCollection creates an empty Collection for the specified NFT type + /// and returns it to the caller so that they can own NFTs + /// @param nftType: The desired nft type to return a collection for. + /// @return An array of NFT Types that the implementing contract defines. + access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} { + post { + result.getIDs().length == 0: + "NonFungibleToken.createEmptyCollection: Cannot create empty collection! " + .concat("The created collection has a non-zero length. ") + .concat("A newly created collection must be empty!") + } + } +} diff --git a/ledger/onflow/contracts/standard/ViewResolver.cdc b/ledger/onflow/contracts/standard/ViewResolver.cdc new file mode 100644 index 0000000..2922b25 --- /dev/null +++ b/ledger/onflow/contracts/standard/ViewResolver.cdc @@ -0,0 +1,58 @@ +// Taken from the NFT Metadata standard, this contract exposes an interface to let +// anyone borrow a contract and resolve views on it. +// +// This will allow you to obtain information about a contract without necessarily knowing anything about it. +// All you need is its address and name and you're good to go! +access(all) contract interface ViewResolver { + + /// Function that returns all the Metadata Views implemented by the resolving contract. + /// Some contracts may have multiple resource types that support metadata views + /// so there is an optional parameter to specify which resource type the caller + /// is requesting views for. + /// Some contract-level views may be type-agnostic. In that case, the contract + /// should return the same views regardless of what type is passed in. + /// + /// @param resourceType: An optional resource type to return views for + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + access(all) view fun getContractViews(resourceType: Type?): [Type] + + /// Function that resolves a metadata view for this token. + /// Some contracts may have multiple resource types that support metadata views + /// so there there is an optional parameter for specify which resource type the caller + /// is looking for views for. + /// Some contract-level views may be type-agnostic. In that case, the contract + /// should return the same views regardless of what type is passed in. + /// + /// @param resourceType: An optional resource type to return views for + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? + + /// Provides access to a set of metadata views. A struct or + /// resource (e.g. an NFT) can implement this interface to provide access to + /// the views that it supports. + /// + access(all) resource interface Resolver { + + /// Same as getViews above, but on a specific NFT instead of a contract + access(all) view fun getViews(): [Type] + + /// Same as resolveView above, but on a specific NFT instead of a contract + access(all) fun resolveView(_ view: Type): AnyStruct? + } + + /// A group of view resolvers indexed by ID. + /// + access(all) resource interface ResolverCollection { + access(all) view fun borrowViewResolver(id: UInt64): &{Resolver}? { + return nil + } + + access(all) view fun getIDs(): [UInt64] { + return [] + } + } +} diff --git a/ledger/onflow/embedded.go b/ledger/onflow/embedded.go new file mode 100644 index 0000000..298b3b8 --- /dev/null +++ b/ledger/onflow/embedded.go @@ -0,0 +1,22 @@ +package onflow + +import ( + "embed" + + "github.com/onflow/flowkit/v2/config" + "github.com/piprate/splash" +) + +//go:embed contracts +//go:embed flow.json +var testdataFS embed.FS + +// NewNetworkConnectorEmbedded creates a new Splash Connector that uses embedded Flow configuration. +func NewNetworkConnectorEmbedded(network string) (*splash.Connector, error) { + return splash.NewNetworkConnector([]string{config.DefaultPath}, splash.NewEmbedLoader(&testdataFS), network, splash.NewZeroLogger()) +} + +// NewInMemoryConnectorEmbedded creates a new Splash Connector for in-memory emulator that uses embedded Flow configuration. +func NewInMemoryConnectorEmbedded(enableTxFees bool) (*splash.Connector, error) { + return splash.NewInMemoryConnector([]string{config.DefaultPath}, splash.NewEmbedLoader(&testdataFS), enableTxFees, splash.NewZeroLogger()) +} diff --git a/ledger/onflow/embedded_test.go b/ledger/onflow/embedded_test.go new file mode 100644 index 0000000..540e790 --- /dev/null +++ b/ledger/onflow/embedded_test.go @@ -0,0 +1,22 @@ +package onflow_test + +import ( + "context" + "testing" + + . "github.com/piprate/metalocker/ledger/onflow" + "github.com/stretchr/testify/require" +) + +func TestNewGoWithTheFlowEmbedded(t *testing.T) { + client, err := NewInMemoryConnectorEmbedded(false) + require.NoError(t, err) + + ctx := context.Background() + + _, err = client.CreateAccountsE(ctx, "emulator-account") + require.NoError(t, err) + + err = client.InitializeContractsE(ctx) + require.NoError(t, err) +} diff --git a/ledger/onflow/emulator/inmemory.go b/ledger/onflow/emulator/inmemory.go new file mode 100644 index 0000000..0ff1451 --- /dev/null +++ b/ledger/onflow/emulator/inmemory.go @@ -0,0 +1,83 @@ +package emulator + +import ( + "context" + "testing" + + "github.com/onflow/cadence" + "github.com/onflow/flow-go-sdk" + "github.com/piprate/metalocker/ledger/onflow" + "github.com/piprate/splash" + "github.com/stretchr/testify/require" +) + +func ConfigureInMemoryEmulator(t *testing.T, client *splash.Connector, adminAccountName string, adminFlowDeposit string) { + t.Helper() + + _, err := client.DoNotPrependNetworkToAccountNames().CreateAccountsE(context.Background(), "emulator-account") + require.NoError(t, err) + + if adminFlowDeposit != "" { + adminAcct := client.Account(adminAccountName) + + se, err := onflow.NewTemplateEngine(client) + require.NoError(t, err) + + FundAccountWithFlow(t, se, adminAcct.Address, adminFlowDeposit) + } + + err = client.InitializeContractsE(context.Background()) + require.NoError(t, err) +} + +func AddLedgerPoolKeys(t *testing.T, client *splash.Connector, srcAccountName string, srcKeyIndex, numKeys int) { + t.Helper() + + tx := client.Transaction(` +transaction(srcIndex: Int, numKeys: Int) { + + prepare(signer: auth(AddContract, AddKey) &Account) { + // copy the main key with 0 weight multiple times + // to create the required number of keys + let key = signer.keys.get(keyIndex: srcIndex)! + var count: Int = 0 + while count < numKeys { + signer.keys.add( + publicKey: key.publicKey, + hashAlgorithm: key.hashAlgorithm, + weight: 0.0 + ) + count = count + 1 + } + } +}`). + IntArgument(srcKeyIndex). + IntArgument(numKeys). + SignProposeAndPayAs(srcAccountName). + Test(t). + AssertSuccess() + require.NoError(t, tx.Err) +} + +func FundAccountWithFlow(t *testing.T, se *splash.TemplateEngine, receiverAddress flow.Address, amount string) { + t.Helper() + + tx := se.NewTransaction("account_fund_flow"). + Argument(cadence.NewAddress(receiverAddress)). + UFix64Argument(amount). + SignProposeAndPayAsService(). + Test(t). + AssertSuccess() + require.NoError(t, tx.Err) +} + +func GetFlowBalance(t *testing.T, se *splash.TemplateEngine, address flow.Address) float64 { + t.Helper() + + v, err := se.NewScript("account_balance_flow"). + Argument(cadence.NewAddress(address)). + RunReturns(context.Background()) + require.NoError(t, err) + + return splash.ToFloat64(v) +} diff --git a/ledger/onflow/flow.json b/ledger/onflow/flow.json new file mode 100644 index 0000000..9b83ee9 --- /dev/null +++ b/ledger/onflow/flow.json @@ -0,0 +1,128 @@ +{ + "contracts": { + "Burner": { + "source": "./contracts/standard/Burner.cdc", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "f233dcee88fe0abe", + "testing": "0000000000000007", + "testnet": "9a0766d93b6608b7" + } + }, + "FlowToken": { + "source": "./contracts/standard/FlowToken.cdc", + "aliases": { + "emulator": "0ae53cb6e3f42a79", + "mainnet": "1654653399040a61", + "testing": "0000000000000003", + "testnet": "7e60df042a9c0868" + } + }, + "FungibleToken": { + "source": "./contracts/standard/FungibleToken.cdc", + "aliases": { + "emulator": "ee82856bf20e2aa6", + "mainnet": "f233dcee88fe0abe", + "testing": "0000000000000007", + "testnet": "9a0766d93b6608b7" + } + }, + "FungibleTokenMetadataViews": { + "source": "./contracts/standard/FungibleTokenMetadataViews.cdc", + "aliases": { + "emulator": "ee82856bf20e2aa6", + "mainnet": "f233dcee88fe0abe", + "testing": "0000000000000007", + "testnet": "9a0766d93b6608b7" + } + }, + "FungibleTokenSwitchboard": { + "source": "./contracts/standard/FungibleTokenSwitchboard.cdc", + "aliases": { + "emulator": "ee82856bf20e2aa6", + "mainnet": "f233dcee88fe0abe", + "testing": "0000000000000007", + "testnet": "9a0766d93b6608b7" + } + }, + "MetadataViews": { + "source": "./contracts/standard/MetadataViews.cdc", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "1d7e57aa55817448", + "testing": "0000000000000001", + "testnet": "631e88ae7f1d7c20" + } + }, + "MetaLocker": { + "source": "./contracts/MetaLocker.cdc", + "aliases": { + "emulator": "179b6b1cb6755e31", + "mainnet": "179b6b1cb6755e31", + "testnet": "179b6b1cb6755e31" + } + }, + "NonFungibleToken": { + "source": "./contracts/standard/NonFungibleToken.cdc", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "1d7e57aa55817448", + "testing": "0000000000000001", + "testnet": "631e88ae7f1d7c20" + } + }, + "ViewResolver": { + "source": "./contracts/standard/ViewResolver.cdc", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "1d7e57aa55817448", + "testing": "0000000000000001", + "testnet": "631e88ae7f1d7c20" + } + } + }, + "networks": { + "emulator": "127.0.0.1:3569", + "mainnet": "access.mainnet.nodes.onflow.org:9000", + "testing": "127.0.0.1:3569", + "testnet": "access.devnet.nodes.onflow.org:9000" + }, + "accounts": { + "emulator-account": { + "address": "f8d6e0586b0a20c7", + "key": "7cc3a167907702780552edebe976d4ad85213d7fd214e7fbc89b587043ec117b" + }, + "emulator-metalocker-admin": { + "address": "179b6b1cb6755e31", + "key": "c70856e54b2b25888ac13ad975ba652d0c95108d60d7df5754fc0649616975de" + }, + "emulator-metalocker-platform": { + "address": "f3fcd2c1a78f5eee", + "key": "80025f0d1f2fd1ba0e18f447681fdd6a68a62ea86c2c2fefa811df086d40db3c" + }, + "emulator-user1": { + "address": "e03daebed8ca0615", + "key": "76e26282dafb246d58778187d37d72efade3679c06ba423855388acdf8bb1e3d" + }, + "emulator-user2": { + "address": "045a1763c93006ca", + "key": "1c6f9682cc8e70128e356f95d13f41d2fc6c9493397b69e0ff0b1f6f2f0ec02b" + }, + "emulator-user3": { + "address": "120e725050340cab", + "key": "93b14b35e2bd2e3d96d0deac56b8199b8a02dedef52c295d1d61c22f494de81b" + } + }, + "deployments": { + "emulator": { + "emulator-account": [], + "emulator-metalocker-admin": [ + "MetaLocker" + ], + "emulator-metalocker-platform": [], + "emulator-user1": [], + "emulator-user2": [], + "emulator-user3": [] + } + } +} diff --git a/ledger/onflow/ledger.go b/ledger/onflow/ledger.go new file mode 100644 index 0000000..44e1efb --- /dev/null +++ b/ledger/onflow/ledger.go @@ -0,0 +1,985 @@ +package onflow + +import ( + "context" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/alitto/pond/v2" + "github.com/gammazero/deque" + "github.com/onflow/cadence" + "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flowkit/v2" + "github.com/onflow/flowkit/v2/accounts" + "github.com/piprate/metalocker/ledger" + "github.com/piprate/metalocker/model" + "github.com/piprate/metalocker/sdk/cmdbase" + "github.com/piprate/metalocker/services/notification" + "github.com/piprate/metalocker/utils" + "github.com/piprate/metalocker/utils/jsonw" + "github.com/piprate/metalocker/utils/measure" + "github.com/piprate/splash" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +const ( + saveScannerProgressIntervalBlocks = 1000 + maxBlocks = 500 + emulatorTimeoutSeconds = 5 +) + +func init() { + ledger.Register("flow", CreateLedgerConnector) +} + +type Ledger struct { + network string + conn *splash.Connector + scriptsEngine *splash.TemplateEngine + nodeAccount *accounts.Account + ns notification.Service + + cacheDBClient *utils.BoltClient + + privateKey crypto.PrivateKey + dequeMutex *sync.Mutex + keyDeque deque.Deque[uint32] + recordWorkerPool pond.ResultPool[model.RecordStatus] + + latestBlockHeight uint64 + latestBlockNumber uint64 + eventTypes []string + + syncMutex *sync.Mutex + + controlCh chan string + stopping bool +} + +var _ model.Ledger = (*Ledger)(nil) + +func (l *Ledger) Close() error { + log.Debug().Msg("Stopping Flow Ledger connector") + + l.StopContinuousSync() + + log.Debug().Msg("Closing Flow Ledger connector") + + return nil +} + +func (l *Ledger) StartLedgerEvents(ctx context.Context, syncOnStart bool, syncInterval time.Duration) error { + if syncOnStart { + if err := l.Sync(ctx); err != nil { + return err + } + } + + if l.conn.IsInMemoryEmulator() { + return l.startPeriodicSync(ctx, syncInterval) + } else { + return l.startBlockSubscription(ctx) + } +} + +func (l *Ledger) startPeriodicSync(ctx context.Context, syncInterval time.Duration) error { + if syncInterval == 0 { + return nil + } + + l.controlCh = make(chan string, 5) + + log.Debug().Dur("int", syncInterval).Msg("Setting Flow Scanner's sync interval") + ticker := time.NewTicker(syncInterval) + + sampledLog := log.Sample(&zerolog.Rarely) + + go func() { + defer func() { + l.controlCh = nil + }() + STOP: + for { + select { + case <-ticker.C: + sampledLog.Debug().Msg("Flow Sync after sync interval") + + if l.stopping { + log.Warn().Msg("Attempted to sync Flow Scanner in a closing state. Quitting...") + ticker.Stop() + break STOP + } + if err := l.Sync(ctx); err != nil { + log.Err(err).Msg("Failed to sync with Flow") + } + case <-l.controlCh: + log.Debug().Msg("Shutting event loop for Flow Scanner") + ticker.Stop() + break STOP + } + } + }() + + return nil +} + +func (l *Ledger) startBlockSubscription(ctx context.Context) error { + l.controlCh = make(chan string, 5) + + sampledLog := log.Sample(&zerolog.Rarely) + + flowClient := l.conn.GRPCClient + + eventFilter := flow.EventFilter{} + + blockEvents, errChan, err := flowClient.SubscribeEventsByBlockHeight(ctx, l.latestBlockHeight, eventFilter) + if err != nil { + return err + } + + reconnect := func(height uint64) { + sampledLog.Warn().Uint64("height", height).Msg("Reconnecting...") + blockEvents, errChan, err = flowClient.SubscribeEventsByBlockHeight(ctx, height, eventFilter) + if err != nil { + sampledLog.Err(err).Msg("Failed to reconnect") + } + } + + go func() { + defer func() { + l.controlCh = nil + }() + + lastSavedHeight := l.latestBlockHeight + STOP: + for { + select { + case <-l.controlCh: + log.Debug().Msg("Shutting event loop for Flow Ledger connector") + break STOP + case <-ctx.Done(): + return + case eventData, ok := <-blockEvents: + if !ok { + if ctx.Err() != nil { + return // graceful shutdown + } + // unexpected close + reconnect(l.latestBlockHeight + 1) + continue + } + + sampledLog.Debug().Uint64("height", eventData.Height).Msg("New Flow block found") + + var eventsFound bool + if len(eventData.Events) > 0 { + if eventsFound, err = l.processEvents(ctx, []flow.BlockEvents{eventData}); err != nil { + log.Err(err).Uint64("height", eventData.Height).Msg("Failed to process Flow events from block") + } + } + + if eventsFound || (eventData.Height-lastSavedHeight > saveScannerProgressIntervalBlocks) { + if err = l.cacheDBClient.Update(ControlsKey, TopFlowBlockHeightKey, []byte(utils.Uint64ToString(eventData.Height))); err != nil { + log.Err(err).Uint64("height", eventData.Height).Msg("Failed to update latest block height") + } + lastSavedHeight = eventData.Height + } + + l.latestBlockHeight = eventData.Height + + case err, ok := <-errChan: + if !ok { + if ctx.Err() != nil { + return // graceful shutdown + } + // unexpected close + reconnect(l.latestBlockHeight + 1) + continue + } + + log.Err(err).Msg("Error scanning Flow blockchain") + reconnect(l.latestBlockHeight + 1) + continue + } + } + }() + + return nil +} + +func (l *Ledger) StopContinuousSync() { + if l.controlCh != nil { + l.stopping = true + l.controlCh <- "stop" + } +} + +func (l *Ledger) Sync(ctx context.Context) error { + l.syncMutex.Lock() + defer l.syncMutex.Unlock() + + block, err := l.conn.Services.GetBlock(ctx, flowkit.LatestBlockQuery) + if err != nil { + return err + } + latestBlockHeight := block.Height + + if latestBlockHeight > l.latestBlockHeight { + return l.sync(ctx, l.latestBlockHeight+1, latestBlockHeight) + } + + return nil +} + +func (l *Ledger) sync(ctx context.Context, startHeight uint64, endHeight uint64) error { + log.Info().Uint64("startHeight", startHeight).Uint64("endHeight", endHeight).Msg("Start scanning Flow block range") + + for currentStartHeight := startHeight; currentStartHeight <= endHeight && !l.stopping; { + currentEndHeight := currentStartHeight + maxBlocks + if currentEndHeight > endHeight { + currentEndHeight = endHeight + } + + log.Info().Uint64("currentStartHeight", currentStartHeight). + Uint64("currentEndHeight", currentEndHeight).Msg("Scanning block sub-range") + + blockEvents, err := l.conn.Services.GetEvents(ctx, l.eventTypes, currentStartHeight, currentEndHeight, &flowkit.EventWorker{ + Count: 100, + BlocksPerWorker: 1, + }) + if err != nil { + return err + } + + if l.stopping { + return errors.New("scanner interrupted") + } + + if _, err = l.processEvents(ctx, blockEvents); err != nil { + return err + } + + l.latestBlockHeight = currentEndHeight + + if err := l.cacheDBClient.Update(ControlsKey, TopFlowBlockHeightKey, []byte(utils.Uint64ToString(currentEndHeight))); err != nil { + return err + } + currentStartHeight += maxBlocks + } + + return nil +} + +func sortEvents(events []flow.BlockEvents) []flow.Event { + sort.SliceStable(events, func(i, j int) bool { + return events[i].Height < events[j].Height + }) + + res := make([]flow.Event, 0) + + var prevHeight uint64 = 0 + var buf []flow.Event + sortEventsFn := func(i, j int) bool { + if buf[i].TransactionIndex != buf[j].TransactionIndex { + return buf[i].TransactionIndex < buf[j].TransactionIndex + } else { + return buf[i].EventIndex < buf[j].EventIndex + } + } + for _, eventBlock := range events { + if eventBlock.Height != prevHeight { + sort.Slice(buf, sortEventsFn) + res = append(res, buf...) + buf = nil + prevHeight = eventBlock.Height + } + buf = append(buf, eventBlock.Events...) + } + + if len(buf) > 0 { + sort.Slice(buf, sortEventsFn) + res = append(res, buf...) + } + + return res +} + +func (l *Ledger) processEvents(ctx context.Context, blockEvents []flow.BlockEvents) (bool, error) { + //ld.PrintDocument("blockEvents", blockEvents) + eventsFound := false + + events := sortEvents(blockEvents) + + var blockNumber uint64 + var blockRecords []*model.Record + + for _, e := range events { + log.Debug().Str("id", e.Value.EventType.QualifiedIdentifier).Msg("NEW EVENT") + switch e.Value.EventType.QualifiedIdentifier { + case "MetaLocker.BlockOpened": + if len(blockRecords) > 0 { + if err := l.saveBlockRecords(blockNumber, blockRecords); err != nil { + return true, err + } + blockRecords = nil + } + + eventsFound = true + b := parseEventBlockOpened(&e) + log.Debug().Uint64("number", b.Number).Msg("NEW OPEN BLOCK RECEIVED") + if err := l.addBlock(b, false); err != nil { + return true, err + } + blockNumber = b.Number + case "MetaLocker.BlockConfirmed": + eventsFound = true + b := parseEventBlockConfirmed(&e) + log.Debug().Uint64("number", b.Number).Msg("NEW CONFIRMED BLOCK RECEIVED") + if err := l.addBlock(b, true); err != nil { + return true, err + } + + case "MetaLocker.RecordPublished": + rid := string(e.Value.SearchFieldByName("id").(cadence.String)) + rec, err := l.GetRecord(ctx, rid) + if err != nil { + return true, err + } + + // the record may be revoked, if it's a rescan. Reset status to Published. + rec.Status = model.StatusPublished + + if err := l.addRecord(rec); err != nil { + return true, err + } + blockRecords = append(blockRecords, rec) + case "MetaLocker.RecordRevoked": + rid := string(e.Value.SearchFieldByName("id").(cadence.String)) + rec, err := l.GetRecord(ctx, rid) + if err != nil { + return true, err + } + + rec.Status = model.StatusRevoked + + if err := l.addRecord(rec); err != nil { + return true, err + } + } + } + if len(blockRecords) > 0 { + if err := l.saveBlockRecords(blockNumber, blockRecords); err != nil { + return true, err + } + } + + return eventsFound, nil +} + +func parseEventBlockOpened(e *flow.Event) *model.Block { + fieldMap := e.Value.FieldsMappedByName() + //chain := fieldMap["chain"].(cadence.Address).String() + id := uint64(fieldMap["id"].(cadence.UInt64)) + hexParentHash := string(fieldMap["parentHash"].(cadence.String)) + parentHash, _ := hex.DecodeString(hexParentHash) + + return &model.Block{ + Number: id, + ParentHash: base64.StdEncoding.EncodeToString(parentHash), + Status: model.BlockStatusOpen, + } +} + +func parseEventBlockConfirmed(e *flow.Event) *model.Block { + fieldMap := e.Value.FieldsMappedByName() + //chain := fieldMap["chain"].(cadence.Address).String() + id := uint64(fieldMap["id"].(cadence.UInt64)) + hexParentHash := string(fieldMap["parentHash"].(cadence.String)) + parentHash, _ := hex.DecodeString(hexParentHash) + hexHash := string(fieldMap["hash"].(cadence.String)) + hash, _ := hex.DecodeString(hexHash) + + return &model.Block{ + Number: id, + Hash: base64.StdEncoding.EncodeToString(hash), + ParentHash: base64.StdEncoding.EncodeToString(parentHash), + Status: model.BlockStatusConfirmed, + } +} + +func (l *Ledger) addBlock(b *model.Block, onConfirmation bool) error { + bb, err := jsonw.Marshal(b) + if err != nil { + return err + } + + key := utils.Uint64ToString(b.Number) + if err = l.cacheDBClient.Update(BlocksKey, key, bb); err != nil { + return err + } + + if l.ns != nil && !onConfirmation { + _ = l.ns.Publish(&model.NewBlockMessage{ + Type: model.MessageTypeNewBlockNotification, + Number: b.Number, + }, false, false, model.NTopicNewBlock) + } + + return nil +} + +func (l *Ledger) loadBlock(bn uint64) (*model.Block, error) { + defer measure.ExecTime("onflow.loadBlock")() + + b, err := l.cacheDBClient.FetchBytes(BlocksKey, utils.Uint64ToString(bn)) + if err != nil { + return nil, err + } + if b != nil { + var bb model.Block + err = jsonw.Unmarshal(b, &bb) + if err != nil { + return nil, err + } + return &bb, nil + } else { + return nil, nil + } +} + +func (l *Ledger) saveBlockRecords(blockNumber uint64, records []*model.Record) error { + body := make([][]string, len(records)) + + for i, rec := range records { + body[i] = []string{ + rec.ID, rec.RoutingKey, strconv.FormatUint(uint64(rec.KeyIndex), 10), + } + } + + bb, err := jsonw.Marshal(body) + if err != nil { + return err + } + + log.Warn().Uint64("blockNumber", blockNumber).Int("len", len(records)).Msg("IN saveBlockRecords") + + return l.cacheDBClient.Update(BlockRecordsKey, utils.Uint64ToString(blockNumber), bb) +} + +func (l *Ledger) loadBlockRecords(blockNumber uint64) ([][]string, error) { + defer measure.ExecTime("onflow.loadBlockRecords")() + + b, err := l.cacheDBClient.FetchBytes(BlockRecordsKey, utils.Uint64ToString(blockNumber)) + if err != nil { + return nil, err + } + if b != nil { + var bb [][]string + err = jsonw.Unmarshal(b, &bb) + if err != nil { + return nil, err + } + return bb, nil + } else { + return nil, model.ErrBlockNotFound + } +} + +func (l *Ledger) addRecord(rec *model.Record) error { + bb, err := jsonw.Marshal(rec) + if err != nil { + return err + } + + if err = l.cacheDBClient.Update(RecordsKey, rec.ID, bb); err != nil { + return err + } + + return nil +} + +func (l *Ledger) loadRecord(rid string) (*model.Record, error) { + defer measure.ExecTime("onflow.loadRecord")() + + b, err := l.cacheDBClient.FetchBytes(RecordsKey, rid) + if err != nil { + return nil, err + } + if b != nil { + var rec model.Record + err = jsonw.Unmarshal(b, &rec) + if err != nil { + return nil, err + } + return &rec, nil + } else { + return nil, nil + } +} + +func (l *Ledger) runTx(ctx context.Context, txBuilder *splash.FlowTransactionBuilder) error { + var heightBeforeTx uint64 + + if l.conn.IsInMemoryEmulator() { + block, err := l.conn.Services.GetBlock(ctx, flowkit.LatestBlockQuery) + if err != nil { + return err + } + heightBeforeTx = block.Height + log.Warn().Uint64("heightBeforeTx", heightBeforeTx).Msg("Start TX") + } + + res, err := txBuilder.RunE(ctx) + if err != nil { + return err + } + + if res.Status == flow.TransactionStatusPending && l.conn.IsInMemoryEmulator() { + // this only happens in unit tests + log.Debug().Uint64("height", heightBeforeTx).Msg("Waiting for transaction to finish") + for i := 0; i < emulatorTimeoutSeconds; i++ { + block, err := l.conn.Services.GetBlock(ctx, flowkit.LatestBlockQuery) + if err != nil { + return err + } + + if block.Height > heightBeforeTx { + log.Debug().Uint64("height_before_tx", heightBeforeTx).Uint64("current_height", block.Height).Msg("New block found") + return nil + } else { + log.Debug().Msg("No new blocks. Sleeping for 1 second...") + time.Sleep(time.Second) + } + } + + return errors.New("in-memory transaction timed out") + } + + return nil +} + +func (l *Ledger) SubmitRecord(ctx context.Context, r *model.Record) error { + _, err := l.recordWorkerPool.SubmitErr(func() (model.RecordStatus, error) { + l.dequeMutex.Lock() + keyIndex := l.keyDeque.PopFront() + l.dequeMutex.Unlock() + defer l.keyDeque.PushBack(keyIndex) + + acct := &accounts.Account{ + Name: "pool", + Address: l.nodeAccount.Address, + Key: accounts.NewHexKeyFromPrivateKey(keyIndex, crypto.SHA3_256, l.privateKey), + } + + for { + txBuilder := l.scriptsEngine.NewTransaction("metalocker_submit_record"). + Argument(RecordToCadence(r, l.scriptsEngine.ContractAddress("MetaLocker"))) + txBuilder.Proposer = acct + txBuilder.Payer = acct + txBuilder.MainSigner = l.nodeAccount + + err := l.runTx(ctx, &txBuilder) + if err != nil { + if strings.HasPrefix(err.Error(), "transaction is expired") { + log.Warn().Err(err).Str("rid", r.ID).Msg("EXPIRED") + continue + } + return "", err + } else { + break + } + } + + return model.StatusPublished, nil + }).Wait() + + return err +} + +func (l *Ledger) GetRecord(ctx context.Context, rid string) (*model.Record, error) { + defer measure.ExecTime("onflow.GetRecord")() + + rec, err := l.loadRecord(rid) + if err != nil { + return nil, err + } + + if rec != nil { + return rec, nil + } else { + val, err := l.scriptsEngine.NewScript("metalocker_get_record"). + Argument(cadence.String(rid)). + RunReturns(ctx) + if err != nil { + return nil, err + } + + rec, err := RecordFromCadence(val) + if err != nil { + return nil, err + } + + if rec != nil { + return rec, nil + } else { + return nil, model.ErrRecordNotFound + } + } + + //val, err := l.scriptsEngine.NewScript("metalocker_get_record"). + // Argument(cadence.String(rid)). + // RunReturns(ctx) + //if err != nil { + // return nil, err + //} + // + //rec, err := RecordFromCadence(val) + //if err != nil { + // return nil, err + //} + // + //if rec != nil { + // return rec, nil + //} else { + // return nil, model.ErrRecordNotFound + //} +} + +func (l *Ledger) GetRecordState(ctx context.Context, rid string) (*model.RecordState, error) { + val, err := l.scriptsEngine.NewScript("metalocker_get_record"). + Argument(cadence.String(rid)). + RunReturns(ctx) + if err != nil { + return nil, err + } + + if opt, ok := val.(cadence.Optional); ok { + if opt.Value == nil { + return nil, nil + } + val = opt.Value + } + + valStruct, ok := val.(cadence.Struct) + if !ok || valStruct.StructType.QualifiedIdentifier != "MetaLocker.Record" { + return nil, errors.New("bad Record value") + } + + return &model.RecordState{ + Status: model.RecordStatus(valStruct.SearchFieldByName("status").(cadence.String)), + BlockNumber: uint64(valStruct.SearchFieldByName("blockNumber").(cadence.UInt64)), + }, nil +} + +func (l *Ledger) GetBlock(ctx context.Context, bn uint64) (*model.Block, error) { + b, err := l.loadBlock(bn) + if err != nil { + return nil, err + } + + if b == nil || b.Status == model.BlockStatusOpen { + val, err := l.scriptsEngine.NewScript("metalocker_get_block"). + Argument(cadence.UInt64(bn)). + RunReturns(ctx) + if err != nil { + return nil, err + } + + b, err = BlockFromCadence(val) + if err != nil { + return nil, err + } + if b == nil { + return nil, model.ErrBlockNotFound + } + + if err = l.addBlock(b, true); err != nil { + return nil, err + } + } + + return b, nil +} + +func (l *Ledger) GetBlockRecords(ctx context.Context, bn uint64) ([][]string, error) { + recList, err := l.loadBlockRecords(bn) + if err != nil { + if !errors.Is(err, model.ErrBlockNotFound) { + return nil, err + } + // load from Flow + val, err := l.scriptsEngine.NewScript("metalocker_get_block"). + Argument(cadence.UInt64(bn)). + RunReturns(ctx) + if err != nil { + return nil, err + } + + recList, err = BlockRecordsFromCadence(val) + if err != nil { + return nil, err + } + } + + return recList, nil +} + +func (l *Ledger) GetGenesisBlock(ctx context.Context) (*model.Block, error) { + return l.GetBlock(ctx, 0) +} + +func (l *Ledger) GetTopBlock(ctx context.Context) (*model.Block, error) { + //return l.loadBlock(l.latestBlockNumber) + + val, err := l.scriptsEngine.NewScript("metalocker_get_top_block_number"). + RunReturns(ctx) + if err != nil { + return nil, err + } + + topBlockNumber := uint64(val.(cadence.UInt64)) + + return &model.Block{ + Number: topBlockNumber, + Hash: "", + ParentHash: "", + Status: 0, + }, nil +} + +func (l *Ledger) GetChain(ctx context.Context, startNumber uint64, depth int) ([]*model.Block, error) { + defer measure.ExecTime("onflow.GetChain")() + + b, err := l.GetBlock(ctx, startNumber) + if err != nil { + return nil, err + } + + top, err := l.GetTopBlock(ctx) + if err != nil { + return nil, err + } + + result := []*model.Block{b} + + last := b.Number + uint64(depth-1) + if last > top.Number { + last = top.Number + } + + for seqNo := b.Number + 1; seqNo <= last; seqNo++ { + b, err = l.GetBlock(ctx, seqNo) + if err != nil { + return nil, err + } + result = append(result, b) + } + return result, nil +} + +func (l *Ledger) GetDataAssetState(ctx context.Context, id string) (model.DataAssetState, error) { + val, err := l.scriptsEngine.NewScript("metalocker_get_data_asset_counter"). + Argument(cadence.String(id)). + RunReturns(ctx) + if err != nil { + return model.DataAssetStateNotFound, err + } + counter := uint64(val.(cadence.UInt64)) + if counter > 0 { + return model.DataAssetStateKeep, nil + } else { + return model.DataAssetStateRemove, nil + } +} + +func (l *Ledger) GetAssetHead(ctx context.Context, headID string) (*model.Record, error) { + val, err := l.scriptsEngine.NewScript("metalocker_get_asset_head"). + Argument(cadence.String(headID)). + RunReturns(ctx) + if err != nil { + return nil, err + } + + rec, err := RecordFromCadence(val) + if err != nil { + return nil, err + } + + if rec == nil { + return nil, model.ErrAssetHeadNotFound + } else { + return rec, nil + } +} + +func NewLedger(ctx context.Context, connector *splash.Connector, network string, nodeAcct *accounts.Account, keyIndexList []uint32, dbFilePath string, ns notification.Service) (*Ledger, error) { + log.Info().Msg("Initialising Flow ledger") + + scriptsEngine, err := NewTemplateEngine(connector) + if err != nil { + return nil, err + } + + wellKnownAddresses := scriptsEngine.WellKnownAddresses() + digitalArtAddr, ok := wellKnownAddresses["MetaLocker"] + if !ok { + return nil, errors.New("contract not found: MetaLocker") + } + + eventTypes := []string{ + fmt.Sprintf("A.%s.MetaLocker.BlockOpened", digitalArtAddr[2:]), + fmt.Sprintf("A.%s.MetaLocker.BlockConfirmed", digitalArtAddr[2:]), + fmt.Sprintf("A.%s.MetaLocker.RecordPublished", digitalArtAddr[2:]), + fmt.Sprintf("A.%s.MetaLocker.RecordRevoked", digitalArtAddr[2:]), + } + + cacheDBClient, err := utils.NewBoltClient(dbFilePath, InstallLedgerSchema) + if err != nil { + return nil, err + } + + genesisBlockHeight, err := cacheDBClient.FetchUint64(ControlsKey, GenesisBlockHeightKey) + if err != nil { + return nil, err + } + + latestBlockNumber, err := cacheDBClient.FetchUint64(ControlsKey, TopBlockNumberKey) + if err != nil { + return nil, err + } + + val, err := scriptsEngine.NewScript("metalocker_get_genesis_block_height"). + RunReturns(ctx) + if err != nil { + return nil, err + } + + actualGenesisBlockHeight := uint64(val.(cadence.UInt64)) + + var latestBlockHeight uint64 + if genesisBlockHeight == 0 { + log.Warn().Uint64("height", actualGenesisBlockHeight).Msg("Initialising Flow ledger with genesis block") + genesisBlockHeight = actualGenesisBlockHeight + + if err = cacheDBClient.UpdateUint64(ControlsKey, GenesisBlockHeightKey, genesisBlockHeight); err != nil { + return nil, err + } + + if err = cacheDBClient.UpdateUint64(ControlsKey, TopBlockNumberKey, genesisBlockHeight); err != nil { + return nil, err + } + latestBlockHeight = genesisBlockHeight - 1 + } else { + if actualGenesisBlockHeight != genesisBlockHeight { + return nil, errors.New("genesis block mismatch") + } + latestBlockHeight, err = cacheDBClient.FetchUint64(ControlsKey, TopFlowBlockHeightKey) + if err != nil { + return nil, err + } + } + + recordWorkerPool := pond.NewResultPool[model.RecordStatus](len(keyIndexList)) + + privateKey, err := nodeAcct.Key.PrivateKey() + if err != nil { + return nil, err + } + + var keyDeque deque.Deque[uint32] + for _, keyIndex := range keyIndexList { + keyDeque.PushBack(keyIndex) + } + + return &Ledger{ + network: network, + nodeAccount: nodeAcct, + conn: connector, + scriptsEngine: scriptsEngine, + ns: ns, + cacheDBClient: cacheDBClient, + privateKey: *privateKey, + dequeMutex: &sync.Mutex{}, + keyDeque: keyDeque, + recordWorkerPool: recordWorkerPool, + latestBlockHeight: latestBlockHeight, + latestBlockNumber: latestBlockNumber, + eventTypes: eventTypes, + syncMutex: &sync.Mutex{}, + }, nil +} + +func loadFlowKitAccount(addrStr string, keyIndex uint32, keyStr string) (*accounts.Account, error) { + acct := &accounts.Account{} + + key, err := crypto.DecodePrivateKeyHex(crypto.ECDSA_P256, keyStr) + if err != nil { + return acct, err + } + + acct.Name = "node" + acct.Address = flow.HexToAddress(addrStr) + acct.Key = accounts.NewHexKeyFromPrivateKey(keyIndex, crypto.SHA3_256, key) + + return acct, nil +} + +func CreateLedgerConnector(ctx context.Context, params ledger.Parameters, ns notification.Service, resolver cmdbase.ParameterResolver) (model.Ledger, error) { + network, ok := params["network"].(string) + if !ok { + return nil, errors.New("parameter not found: network. Can't start Flow ledger connector") + } + dbFilePath, ok := params["dbFile"].(string) + if !ok { + return nil, errors.New("parameter not found: dbFile. Can't start Flow ledger connector") + } + + if network == "local" { + network = "emulator" + } + + flowConnector, err := NewNetworkConnectorEmbedded(network) + if err != nil { + log.Err(err).Msg("Failed to create Flow connector") + return nil, err + } + + nodeAddress, err := resolver.ResolveString(params["nodeAddress"]) + if err != nil { + return nil, err + } + + nodeKey, err := resolver.ResolveString(params["nodeKey"]) + if err != nil { + return nil, err + } + + keyIndexStr, err := resolver.ResolveString(params["keyIndex"]) + if err != nil { + return nil, err + } + + if keyIndexStr == "" { + keyIndexStr = "0" + } + + keyIndex := uint32(utils.StringToUint64(keyIndexStr)) + + nodeAcct, err := loadFlowKitAccount(nodeAddress, keyIndex, nodeKey) + if err != nil { + log.Err(err).Msg("Invalid Flow Node account") + return nil, err + } + + bl, err := NewLedger(ctx, flowConnector, network, nodeAcct, []uint32{0}, utils.AbsPathify(dbFilePath), ns) + return bl, err +} diff --git a/ledger/onflow/ledger_test.go b/ledger/onflow/ledger_test.go new file mode 100644 index 0000000..46a7e1d --- /dev/null +++ b/ledger/onflow/ledger_test.go @@ -0,0 +1,403 @@ +package onflow_test + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "sync" + "testing" + "time" + + "github.com/piprate/json-gold/ld" + . "github.com/piprate/metalocker/ledger/onflow" + "github.com/piprate/metalocker/ledger/onflow/emulator" + "github.com/piprate/metalocker/model" + "github.com/piprate/metalocker/services/notification" + "github.com/piprate/metalocker/utils/jsonw" + "github.com/piprate/splash" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func init() { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Stamp}) +} + +func NewTestLedger(t *testing.T, workerCount int) (*Ledger, *splash.Connector, string) { + t.Helper() + + flowConnector, err := NewInMemoryConnectorEmbedded(true) + require.NoError(t, err) + + emulator.ConfigureInMemoryEmulator(t, flowConnector, adminAccountName, "1000.0") + + dir, err := os.MkdirTemp(".", "tempdir_") + require.NoError(t, err) + + dbFilepath := filepath.Join(dir, "db.bolt") + + ns := notification.NewLocalNotificationService(workerCount * 10) + + se, err := NewTemplateEngine(flowConnector) + require.NoError(t, err) + + nodeAcct := flowConnector.Account(platformAccountName) + emulator.FundAccountWithFlow(t, se, nodeAcct.Address, "10.0") + + emulator.AddLedgerPoolKeys(t, flowConnector, platformAccountName, 0, workerCount) + + keyIndexes := make([]uint32, workerCount) + for i := 0; i < workerCount; i++ { + keyIndexes[i] = uint32(i + 1) + } + + fl, err := NewLedger(context.Background(), flowConnector, "emulator", nodeAcct, keyIndexes, dbFilepath, ns) + require.NoError(t, err) + + return fl, flowConnector, dir +} + +func TestNewLedger(t *testing.T) { + flowConnector, err := splash.NewInMemoryTestConnector(".", true) + require.NoError(t, err) + + emulator.ConfigureInMemoryEmulator(t, flowConnector, adminAccountName, "1000.0") + + dir, err := os.MkdirTemp(".", "tempdir_") + require.NoError(t, err) + defer func() { _ = os.RemoveAll(dir) }() + + nodeAcct := flowConnector.Account(platformAccountName) + + ctx := context.Background() + lr, err := NewLedger(ctx, flowConnector, "emulator", nodeAcct, []uint32{0}, filepath.Join(dir, "db.bolt"), nil) + require.NoError(t, err) + + assert.NoError(t, lr.Close()) +} + +func TestLedger_GetGenesisBlock(t *testing.T) { + fl, _, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + ctx := context.Background() + require.NoError(t, fl.Sync(ctx)) + + gb, err := fl.GetGenesisBlock(context.Background()) + require.NoError(t, err) + assert.NotNil(t, gb) + + ld.PrintDocument("GEN", gb) +} + +func TestLedger_SubmitRecord_Single(t *testing.T) { + fl, _, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var rec *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &rec)) + + ctx := context.Background() + + err := fl.SubmitRecord(ctx, rec) + require.NoError(t, err) + + require.NoError(t, fl.Sync(ctx)) + + loadedRec, err := fl.GetRecord(ctx, rec.ID) + require.NoError(t, err) + + assert.Equal(t, model.StatusPublished, loadedRec.Status) + loadedRec.Status = "" + + assert.Equal(t, rec, loadedRec) +} + +func TestLedger_SubmitRecord_Multiple_SequentialTX(t *testing.T) { + fl, conn, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var template *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &template)) + + conn.DisableAutoMine() + + recordCount := 10 + + var wg sync.WaitGroup + wg.Add(recordCount) + + for i := 0; i < recordCount; i++ { + go func() { + defer wg.Done() + + ctx := context.Background() + + rec := template.Copy() + rec.ID = fmt.Sprintf("record-%d", i+1) + + err := fl.SubmitRecord(ctx, rec) + require.NoError(t, err) + + }() + } + + time.Sleep(time.Second * 1) + + _, res, err := conn.ExecuteAndCommitBlock(t) + require.NoError(t, err) + require.Len(t, res, 1) + + conn.EnableAutoMine() + + wg.Wait() + + ctx := context.Background() + for i := 0; i < recordCount; i++ { + loadedRec, err := fl.GetRecord(ctx, fmt.Sprintf("record-%d", i+1)) + require.NoError(t, err) + require.NotNil(t, loadedRec) + assert.Equal(t, model.StatusPublished, loadedRec.Status) + } +} + +func TestLedger_SubmitRecord_Multiple_OneTX(t *testing.T) { + workerCount := 10 + + fl, conn, dir := NewTestLedger(t, workerCount) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var template *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &template)) + + conn.DisableAutoMine() + + recordCount := workerCount + + var wg sync.WaitGroup + wg.Add(recordCount) + + for i := 0; i < recordCount; i++ { + go func() { + defer wg.Done() + + ctx := context.Background() + + rec := template.Copy() + rec.ID = fmt.Sprintf("record-%d", i+1) + + err := fl.SubmitRecord(ctx, rec) + require.NoError(t, err) + + }() + } + + time.Sleep(time.Second * 1) + + _, res, err := conn.ExecuteAndCommitBlock(t) + require.NoError(t, err) + require.Len(t, res, recordCount) + + wg.Wait() + + ctx := context.Background() + for i := 0; i < recordCount; i++ { + loadedRec, err := fl.GetRecord(ctx, fmt.Sprintf("record-%d", i+1)) + require.NoError(t, err) + require.NotNil(t, loadedRec) + assert.Equal(t, model.StatusPublished, loadedRec.Status) + } +} + +func TestLedger_SubmitRecord_Multiple_GroupedTX(t *testing.T) { + workerCount := 2 + recordCount := 6 + + fl, conn, dir := NewTestLedger(t, workerCount) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var template *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &template)) + + conn.DisableAutoMine() + + var wg sync.WaitGroup + wg.Add(recordCount) + + for i := 0; i < recordCount; i++ { + go func() { + defer wg.Done() + + ctx := context.Background() + + rec := template.Copy() + rec.ID = fmt.Sprintf("record-%d", i+1) + + err := fl.SubmitRecord(ctx, rec) + require.NoError(t, err) + + }() + } + + recordsSubmitted := 0 + + for recordsSubmitted < recordCount { + time.Sleep(time.Second * 1) + + _, res, err := conn.ExecuteAndCommitBlock(t) + require.NoError(t, err) + require.Len(t, res, workerCount) + + recordsSubmitted += workerCount + } + + wg.Wait() + + ctx := context.Background() + for i := 0; i < recordCount; i++ { + loadedRec, err := fl.GetRecord(ctx, fmt.Sprintf("record-%d", i+1)) + require.NoError(t, err) + require.NotNil(t, loadedRec) + assert.Equal(t, model.StatusPublished, loadedRec.Status) + } +} + +func TestLedger_GetBlock(t *testing.T) { + fl, _, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var rec *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &rec)) + + ctx := context.Background() + + b, err := fl.GetBlock(ctx, 0) + require.NoError(t, err) + require.Equal(t, model.BlockStatusConfirmed, b.Status) + + ld.PrintDocument("B0", b) + + b, err = fl.GetBlock(ctx, 1) + require.NoError(t, err) + require.Equal(t, model.BlockStatusOpen, b.Status) + + err = fl.SubmitRecord(ctx, rec) + require.NoError(t, err) + + b, err = fl.GetBlock(ctx, 1) + require.NoError(t, err) + require.Equal(t, model.BlockStatusConfirmed, b.Status) + + b, err = fl.GetBlock(ctx, 2) + require.NoError(t, err) + require.Equal(t, model.BlockStatusOpen, b.Status) + + _, err = fl.GetBlock(ctx, 3) + require.Error(t, err) +} + +func TestLedger_GetBlockRecords(t *testing.T) { + workerCount := 3 + recordCount := 4 + + fl, conn, dir := NewTestLedger(t, workerCount) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var template *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &template)) + + conn.DisableAutoMine() + + var wg sync.WaitGroup + wg.Add(recordCount) + + for i := 0; i < recordCount; i++ { + go func() { + defer wg.Done() + + ctx := context.Background() + + rec := template.Copy() + rec.ID = fmt.Sprintf("record-%d", i+1) + + err := fl.SubmitRecord(ctx, rec) + require.NoError(t, err) + + }() + } + + recordsSubmitted := 0 + + for recordsSubmitted < recordCount { + time.Sleep(time.Second * 1) + + _, res, err := conn.ExecuteAndCommitBlock(t) + require.NoError(t, err) + + recordsSubmitted += len(res) + } + + wg.Wait() + + ctx := context.Background() + + //require.NoError(t, fl.Sync(ctx)) + + blockRecords, err := fl.GetBlockRecords(ctx, 1) + require.NoError(t, err) + + ld.PrintDocument("BLOCK", blockRecords) +} + +func TestLedger_GetRecordState(t *testing.T) { + fl, _, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var rec *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &rec)) + + ctx := context.Background() + + err := fl.SubmitRecord(ctx, rec) + require.NoError(t, err) + + require.NoError(t, fl.Sync(ctx)) + + state, err := fl.GetRecordState(ctx, rec.ID) + require.NoError(t, err) + + assert.Equal(t, model.StatusPublished, state.Status) + assert.True(t, state.BlockNumber > 0) +} + +func TestLedger_GetAssetHead(t *testing.T) { + +} diff --git a/ledger/onflow/templates.go b/ledger/onflow/templates.go new file mode 100644 index 0000000..6475247 --- /dev/null +++ b/ledger/onflow/templates.go @@ -0,0 +1,22 @@ +package onflow + +import ( + "embed" + + "github.com/piprate/splash" +) + +//go:embed templates +var templateFS embed.FS + +var ( + requiredWellKnownContracts = []string{ + "Burner", "FlowToken", "FungibleToken", "FungibleTokenMetadataViews", + "FungibleTokenSwitchboard", "MetadataViews", + "NonFungibleToken", "MetaLocker", + } +) + +func NewTemplateEngine(client *splash.Connector) (*splash.TemplateEngine, error) { + return splash.NewTemplateEngine(client, templateFS, []string{"templates/transactions", "templates/scripts"}, requiredWellKnownContracts) +} diff --git a/ledger/onflow/templates/scripts/account_balance_flow.cdc b/ledger/onflow/templates/scripts/account_balance_flow.cdc new file mode 100644 index 0000000..ebde36c --- /dev/null +++ b/ledger/onflow/templates/scripts/account_balance_flow.cdc @@ -0,0 +1,13 @@ +{{ define "account_balance_flow" }} +// This script reads the balance field of an account's FlowToken Balance +import FlowToken from {{.FlowToken}} + +access(all) fun main(account: Address): UFix64 { + + let vaultRef = getAccount(account) + .capabilities.borrow<&FlowToken.Vault>(/public/flowTokenBalance) + ?? panic("Could not borrow Balance reference to the Vault") + + return vaultRef.balance +} +{{ end }} diff --git a/ledger/onflow/templates/scripts/metalocker_get_asset_head.cdc b/ledger/onflow/templates/scripts/metalocker_get_asset_head.cdc new file mode 100644 index 0000000..6fc867a --- /dev/null +++ b/ledger/onflow/templates/scripts/metalocker_get_asset_head.cdc @@ -0,0 +1,7 @@ +{{ define "metalocker_get_asset_head" }} +import MetaLocker from {{.MetaLocker}} + +access(all) fun main(headID: String) : MetaLocker.Record? { + return MetaLocker.getAssetHead(headID: headID) +} +{{ end }} diff --git a/ledger/onflow/templates/scripts/metalocker_get_block.cdc b/ledger/onflow/templates/scripts/metalocker_get_block.cdc new file mode 100644 index 0000000..fc48b7f --- /dev/null +++ b/ledger/onflow/templates/scripts/metalocker_get_block.cdc @@ -0,0 +1,7 @@ +{{ define "metalocker_get_block" }} +import MetaLocker from {{.MetaLocker}} + +access(all) fun main(number: UInt64) : MetaLocker.MetaBlock? { + return MetaLocker.getBlock(number: number) +} +{{ end }} diff --git a/ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc b/ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc new file mode 100644 index 0000000..0ec5907 --- /dev/null +++ b/ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc @@ -0,0 +1,7 @@ +{{ define "metalocker_get_data_asset_counter" }} +import MetaLocker from {{.MetaLocker}} + +access(all) fun main(id: String) : UInt64 { + return MetaLocker.getDataAssetCounter(id: id) +} +{{ end }} diff --git a/ledger/onflow/templates/scripts/metalocker_get_genesis_block_height.cdc b/ledger/onflow/templates/scripts/metalocker_get_genesis_block_height.cdc new file mode 100644 index 0000000..0662886 --- /dev/null +++ b/ledger/onflow/templates/scripts/metalocker_get_genesis_block_height.cdc @@ -0,0 +1,7 @@ +{{ define "metalocker_get_genesis_block_height" }} +import MetaLocker from {{.MetaLocker}} + +access(all) fun main() : UInt64 { + return MetaLocker.getGenesisBlockHeight() +} +{{ end }} diff --git a/ledger/onflow/templates/scripts/metalocker_get_record.cdc b/ledger/onflow/templates/scripts/metalocker_get_record.cdc new file mode 100644 index 0000000..faa7520 --- /dev/null +++ b/ledger/onflow/templates/scripts/metalocker_get_record.cdc @@ -0,0 +1,7 @@ +{{ define "metalocker_get_record" }} +import MetaLocker from {{.MetaLocker}} + +access(all) fun main(id: String) : MetaLocker.Record? { + return MetaLocker.getRecord(id: id) +} +{{ end }} diff --git a/ledger/onflow/templates/scripts/metalocker_get_top_block_height.cdc b/ledger/onflow/templates/scripts/metalocker_get_top_block_height.cdc new file mode 100644 index 0000000..4536794 --- /dev/null +++ b/ledger/onflow/templates/scripts/metalocker_get_top_block_height.cdc @@ -0,0 +1,7 @@ +{{ define "metalocker_get_top_block_height" }} +import MetaLocker from {{.MetaLocker}} + +access(all) fun main() : UInt64 { + return MetaLocker.getTopBlockHeight() +} +{{ end }} diff --git a/ledger/onflow/templates/scripts/metalocker_get_top_block_number.cdc b/ledger/onflow/templates/scripts/metalocker_get_top_block_number.cdc new file mode 100644 index 0000000..2dcca84 --- /dev/null +++ b/ledger/onflow/templates/scripts/metalocker_get_top_block_number.cdc @@ -0,0 +1,7 @@ +{{ define "metalocker_get_top_block_number" }} +import MetaLocker from {{.MetaLocker}} + +access(all) fun main() : UInt64 { + return MetaLocker.getTopBlock() +} +{{ end }} diff --git a/ledger/onflow/templates/transactions/account_fund_flow.cdc b/ledger/onflow/templates/transactions/account_fund_flow.cdc new file mode 100644 index 0000000..5fd747e --- /dev/null +++ b/ledger/onflow/templates/transactions/account_fund_flow.cdc @@ -0,0 +1,37 @@ +{{ define "account_fund_flow" }} +import FungibleToken from {{.FungibleToken}} +import FlowToken from {{.FlowToken}} + +/// This transaction mints tokens using the account that stores the Flow Token Admin resource +/// This is the service account + +transaction(recipient: Address, amount: UFix64) { + + let tokenAdmin: &FlowToken.Administrator + let tokenReceiver: &{FungibleToken.Receiver} + + prepare(signer: auth(BorrowValue) &Account) { + + self.tokenAdmin = signer.storage + .borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin) + ?? panic("Cannot mint: Signer does not store the FlowToken Admin Resource in their account" + .concat(" at the path /storage/flowTokenAdmin.")) + + self.tokenReceiver = getAccount(recipient) + .capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) + ?? panic("Could not borrow a Receiver reference to the FlowToken Vault in account " + .concat(recipient.toString()).concat(" at path /public/flowTokenReceiver") + .concat(". Make sure you are sending to an address that has ") + .concat("a FlowToken Vault set up properly at the specified path.")) + } + + execute { + let minter <- self.tokenAdmin.createNewMinter(allowedAmount: amount) + let mintedVault <- minter.mintTokens(amount: amount) + + self.tokenReceiver.deposit(from: <-mintedVault) + + destroy minter + } +} +{{ end }} diff --git a/ledger/onflow/templates/transactions/metalocker_submit_record.cdc b/ledger/onflow/templates/transactions/metalocker_submit_record.cdc new file mode 100644 index 0000000..1019365 --- /dev/null +++ b/ledger/onflow/templates/transactions/metalocker_submit_record.cdc @@ -0,0 +1,9 @@ +{{ define "metalocker_submit_record" }} +import MetaLocker from {{.MetaLocker}} + +transaction(rec: MetaLocker.Record) { + execute { + MetaLocker.submitRecord(record: rec) + } +} +{{ end }} diff --git a/ledger/onflow/templates_test.go b/ledger/onflow/templates_test.go new file mode 100644 index 0000000..d48ef0f --- /dev/null +++ b/ledger/onflow/templates_test.go @@ -0,0 +1,74 @@ +package onflow_test + +import ( + "context" + "os" + "testing" + "time" + + "github.com/onflow/cadence" + . "github.com/piprate/metalocker/ledger/onflow" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/require" +) + +func init() { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Stamp}) +} + +func TestNewEngine_emulator(t *testing.T) { + client, err := NewInMemoryConnectorEmbedded(false) + require.NoError(t, err) + + ctx := context.Background() + + _, err = client.CreateAccountsE(ctx, "emulator-account") + require.NoError(t, err) + + err = client.InitializeContractsE(ctx) + require.NoError(t, err) + + _, err = NewTemplateEngine(client) + require.NoError(t, err) +} + +func TestNewEngine_emulatorWithFees(t *testing.T) { + client, err := NewInMemoryConnectorEmbedded(true) + require.NoError(t, err) + + ctx := context.Background() + + _, err = client.DoNotPrependNetworkToAccountNames().CreateAccountsE(ctx, "emulator-account") + require.NoError(t, err) + + te, err := NewTemplateEngine(client) + require.NoError(t, err) + + adminAcct := client.Account("emulator-metalocker-admin") + _ = te.NewTransaction("account_fund_flow"). + Argument(cadence.NewAddress(adminAcct.Address)). + UFix64Argument("1000.0"). + SignProposeAndPayAsService(). + Test(t). + AssertSuccess() + + err = client.InitializeContractsE(ctx) + require.NoError(t, err) +} + +func TestNewEngine_testnet(t *testing.T) { + client, err := NewNetworkConnectorEmbedded("testnet") + require.NoError(t, err) + + _, err = NewTemplateEngine(client) + require.NoError(t, err) +} + +func TestNewEngine_mainnet(t *testing.T) { + client, err := NewNetworkConnectorEmbedded("mainnet") + require.NoError(t, err) + + _, err = NewTemplateEngine(client) + require.NoError(t, err) +} diff --git a/ledger/onflow/types.go b/ledger/onflow/types.go new file mode 100644 index 0000000..9d7cd40 --- /dev/null +++ b/ledger/onflow/types.go @@ -0,0 +1,268 @@ +package onflow + +import ( + "encoding/base64" + "errors" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + "github.com/onflow/flow-go-sdk" + "github.com/piprate/metalocker/model" +) + +func RecordFromCadence(val cadence.Value) (*model.Record, error) { + if opt, ok := val.(cadence.Optional); ok { + if opt.Value == nil { + return nil, nil + } + val = opt.Value + } + + if val == nil { + return nil, nil + } + + valStruct, ok := val.(cadence.Struct) + if !ok || valStruct.StructType.QualifiedIdentifier != "MetaLocker.Record" || len(valStruct.FieldsMappedByName()) != 20 { + return nil, errors.New("bad Record value") + } + + allFields := valStruct.FieldsMappedByName() + + res := &model.Record{ + ID: string(allFields["id"].(cadence.String)), + RoutingKey: string(allFields["routingKey"].(cadence.String)), + KeyIndex: uint32(allFields["keyIndex"].(cadence.UInt32)), + Operation: model.OpType(allFields["operationType"].(cadence.UInt32)), + OperationAddress: string(allFields["address"].(cadence.String)), + Flags: uint32(allFields["flags"].(cadence.UInt32)), + AuthorisingCommitment: cadenceArrayToBase64String(allFields["ac"].(cadence.Array)), + AuthorisingCommitmentType: uint8(allFields["acType"].(cadence.UInt8)), + RequestingCommitment: cadenceArrayToBase64String(allFields["rc"].(cadence.Array)), + RequestingCommitmentType: uint8(allFields["rcType"].(cadence.UInt8)), + ImpressionCommitment: cadenceArrayToBase64String(allFields["ic"].(cadence.Array)), + ImpressionCommitmentType: uint8(allFields["icType"].(cadence.UInt8)), + SubjectRecord: string(allFields["subjectRecord"].(cadence.String)), + HeadID: string(allFields["headID"].(cadence.String)), + HeadBody: string(allFields["headBody"].(cadence.String)), + Signature: string(allFields["signature"].(cadence.String)), + Status: model.RecordStatus(allFields["status"].(cadence.String)), + } + + dataAssets, ok := allFields["dataAssets"].(cadence.Array) + if !ok { + return nil, errors.New("bad dataAssets value") + } + for _, stringVal := range dataAssets.Values { + res.DataAssets = append(res.DataAssets, string(stringVal.(cadence.String))) + } + revocationProof, ok := allFields["revocationProof"].(cadence.Array) + if !ok { + return nil, errors.New("bad revocationProof value") + } + for _, uint8ArrayVal := range revocationProof.Values { + res.RevocationProof = append(res.RevocationProof, cadenceArrayToBase64String(uint8ArrayVal.(cadence.Array))) + } + + return res, nil +} + +func stringArrayToCadence(array []string) cadence.Array { + res := make([]cadence.Value, len(array)) + for i, val := range array { + res[i] = cadence.String(val) + } + return cadence.NewArray(res) +} + +func base64StringToCadence(s string) cadence.Array { + valBytes, _ := base64.StdEncoding.DecodeString(s) + res := make([]cadence.Value, len(valBytes)) + for i, val := range valBytes { + res[i] = cadence.UInt8(val) + } + return cadence.NewArray(res) +} + +func cadenceArrayToBase64String(val cadence.Array) string { + byteArray := make([]byte, len(val.Values)) + for i, int8Val := range val.Values { + byteArray[i] = byte(int8Val.(cadence.UInt8)) + } + return base64.StdEncoding.EncodeToString(byteArray) +} + +func base64StringArrayToCadence(sa []string) cadence.Array { + res := make([]cadence.Value, len(sa)) + for i, s := range sa { + res[i] = base64StringToCadence(s) + } + + return cadence.NewArray(res) +} + +func RecordToCadence(rec *model.Record, contractAddr flow.Address) cadence.Value { + return cadence.NewStruct([]cadence.Value{ + cadence.String(rec.ID), + cadence.String(rec.RoutingKey), + cadence.UInt32(rec.KeyIndex), + cadence.UInt32(rec.Operation), + cadence.String(rec.OperationAddress), + cadence.UInt32(rec.Flags), + base64StringToCadence(rec.AuthorisingCommitment), + cadence.UInt8(rec.AuthorisingCommitmentType), + base64StringToCadence(rec.RequestingCommitment), + cadence.UInt8(rec.RequestingCommitmentType), + base64StringToCadence(rec.ImpressionCommitment), + cadence.UInt8(rec.ImpressionCommitmentType), + stringArrayToCadence(rec.DataAssets), + cadence.String(rec.SubjectRecord), + base64StringArrayToCadence(rec.RevocationProof), + cadence.String(rec.HeadID), + cadence.String(rec.HeadBody), + cadence.String(rec.Signature), + cadence.String(rec.Status), + cadence.UInt64(0), + }).WithType(cadence.NewStructType( + common.AddressLocation{ + Address: common.Address(contractAddr), + Name: common.AddressLocationPrefix, + }, + "MetaLocker.Record", + recordCadenceFields, + nil, + )) +} + +var recordCadenceFields = []cadence.Field{ + { + Identifier: "id", + Type: cadence.StringType, + }, + { + Identifier: "routingKey", + Type: cadence.StringType, + }, + { + Identifier: "keyIndex", + Type: cadence.UInt32Type, + }, + { + Identifier: "operationType", + Type: cadence.UInt32Type, + }, + { + Identifier: "address", + Type: cadence.StringType, + }, + { + Identifier: "flags", + Type: cadence.UInt32Type, + }, + { + Identifier: "ac", + Type: cadence.NewVariableSizedArrayType(cadence.UInt8Type), + }, + { + Identifier: "acType", + Type: cadence.UInt8Type, + }, + { + Identifier: "rc", + Type: cadence.NewVariableSizedArrayType(cadence.UInt8Type), + }, + { + Identifier: "rcType", + Type: cadence.UInt8Type, + }, { + Identifier: "ic", + Type: cadence.NewVariableSizedArrayType(cadence.UInt8Type), + }, + { + Identifier: "icType", + Type: cadence.UInt8Type, + }, + { + Identifier: "dataAssets", + Type: cadence.NewVariableSizedArrayType(cadence.StringType), + }, + { + Identifier: "subjectRecord", + Type: cadence.StringType, + }, + { + Identifier: "revocationProof", + Type: cadence.NewVariableSizedArrayType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)), + }, + { + Identifier: "headID", + Type: cadence.StringType, + }, + { + Identifier: "headBody", + Type: cadence.StringType, + }, + { + Identifier: "signature", + Type: cadence.StringType, + }, + { + Identifier: "status", + Type: cadence.StringType, + }, + { + Identifier: "blockNumber", + Type: cadence.UInt64Type, + }, +} + +func BlockFromCadence(val cadence.Value) (*model.Block, error) { + if opt, ok := val.(cadence.Optional); ok { + if opt.Value == nil { + return nil, nil + } + val = opt.Value + } + + valStruct, ok := val.(cadence.Struct) + if !ok || valStruct.StructType.QualifiedIdentifier != "MetaLocker.MetaBlock" { + return nil, errors.New("bad MetaBlock value") + } + + allFields := valStruct.FieldsMappedByName() + + return &model.Block{ + Number: uint64(allFields["number"].(cadence.UInt64)), + Hash: string(allFields["hash"].(cadence.String)), + ParentHash: string(allFields["parentHash"].(cadence.String)), + Status: model.BlockStatus(allFields["status"].(cadence.UInt8)), + }, nil +} + +func BlockRecordsFromCadence(val cadence.Value) ([][]string, error) { + if opt, ok := val.(cadence.Optional); ok { + if opt.Value == nil { + return nil, nil + } + val = opt.Value + } + + valStruct, ok := val.(cadence.Struct) + if !ok || valStruct.StructType.QualifiedIdentifier != "MetaLocker.MetaBlock" { + return nil, errors.New("bad MetaBlock value") + } + + recsField := valStruct.SearchFieldByName("records").(cadence.Array) + + records := make([][]string, len(recsField.Values)) + for i, elem := range recsField.Values { + arrayVal := elem.(cadence.Array) + rec := make([]string, len(arrayVal.Values)) + for j, val := range arrayVal.Values { + rec[j] = string(val.(cadence.String)) + } + records[i] = rec + } + + return records, nil +} diff --git a/ledger/onflow/types_test.go b/ledger/onflow/types_test.go new file mode 100644 index 0000000..d85fd89 --- /dev/null +++ b/ledger/onflow/types_test.go @@ -0,0 +1,50 @@ +package onflow_test + +import ( + "os" + "strings" + "testing" + "time" + + "github.com/onflow/flow-go-sdk" + . "github.com/piprate/metalocker/ledger/onflow" + "github.com/piprate/metalocker/model" + "github.com/piprate/metalocker/utils/jsonw" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/require" +) + +func init() { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Stamp}) +} + +func TestRecordToCadence(t *testing.T) { + var rec *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(` +{ + "id": "47Gv8nkNgEr95gRZd98c4NAhi4wszmUBk6tytk4TtVCo", + "routingKey": "db5ryf4F6JQjD3aRqMF13RdueHkeHU9WcAx9dEVfy1mq", + "keyIndex": 4, + "operationType": 1, + "address": "CW1bDDn1Pp3W486rVxkrHKLUTirqytPMjBfFfXrqihU8", + "ac": "5z//FjtYNAChMv+8dpMqIiM4uKkGv0BXWx2yu6avIgE=", + "rc": "pVbAvIkXtnzmF7z3U6n4GVQwgjjhQgvw/bomh1Fm72o=", + "rcType": 1, + "dataAssets": [ + "Gp7ZX5S5NECqoMz3u7micC7TuhvWB9z8ErAkympVgt4A" + ], + "signature": "AN1rKvtoGVPioTYojunoBTWWx6MTdb8VHB8vN8takPu4fpe5K7LFowgqW8meuLwniFosM2xCBbHkdhqUoTovr4xDCzKF2JNZb" +} +`), &rec)) + require.NoError(t, rec.Validate()) + + testAddr := flow.HexToAddress("179b6b1cb6755e31") + c := RecordToCadence(rec, testAddr) + require.NotNil(t, c) + + rec2, err := RecordFromCadence(c) + require.NoError(t, err) + + require.NoError(t, rec2.Validate()) +} diff --git a/model/block.go b/model/block.go index a4d79d2..7d415bd 100644 --- a/model/block.go +++ b/model/block.go @@ -18,8 +18,18 @@ package model // Blocks are identified by their sequential numbers, starting with 0. // Hash and ParentHash fields allow connecting a specific block // with the underlying block implementation. + +type BlockStatus uint8 + +const ( + BlockStatusOK BlockStatus = 0 + BlockStatusOpen BlockStatus = 1 + BlockStatusConfirmed BlockStatus = 2 +) + type Block struct { - Number uint64 `json:"number"` - Hash string `json:"hash"` - ParentHash string `json:"parentHash,omitempty"` + Number uint64 `json:"number"` + Hash string `json:"hash,omitempty"` + ParentHash string `json:"parentHash,omitempty"` + Status BlockStatus `json:"status,omitempty"` } diff --git a/model/dataset/wait.go b/model/dataset/wait.go index bbb894a..cb2eb12 100644 --- a/model/dataset/wait.go +++ b/model/dataset/wait.go @@ -39,9 +39,8 @@ func WaitForConfirmation(ctx context.Context, ledger model.Ledger, ns notificati return 0, err } - // check if all the previous record got published + // check if all the previous records got published if len(recordID) > 1 { - var blockNumber uint64 = 0 for _, rid := range recordID[0 : len(recordID)-1] { currentState, err := ledger.GetRecordState(ctx, rid) if err != nil { diff --git a/model/head.go b/model/head.go index 85e63a3..62b4bca 100644 --- a/model/head.go +++ b/model/head.go @@ -20,8 +20,8 @@ import ( "github.com/btcsuite/btcd/btcutil/base58" ) -func HeadID(assetID string, lockerID string, sender *LockerParticipant, headName string) string { - data := strings.Join([]string{assetID, lockerID, sender.SharedSecret}, "|") +func HeadID(assetID string, lockerID string, sharedSecret string, headName string) string { + data := strings.Join([]string{assetID, lockerID, sharedSecret}, "|") return base58.Encode(Hash(headName, []byte(data))) } diff --git a/model/head_test.go b/model/head_test.go index e4cce00..3952465 100644 --- a/model/head_test.go +++ b/model/head_test.go @@ -44,7 +44,7 @@ func TestHeadID(t *testing.T) { assetID := "did:piprate:Fw4CEkwm3n3gMcRGtC9r2aDR7iuTLkciJkkmMPi6b7Km" - headID := HeadID(assetID, locker.ID, locker.Us(), "main") + headID := HeadID(assetID, locker.ID, locker.Us().SharedSecret, "main") assert.Equal(t, "2BfG8PvqKFSKpGWLyQfnmsqE3m3YAfQaDacCMw58t41n", headID) } diff --git a/model/locker.go b/model/locker.go index daab25e..a74956a 100644 --- a/model/locker.go +++ b/model/locker.go @@ -170,8 +170,8 @@ func (lp *LockerParticipant) GetRecordPrivateKey(idx uint32) (*hdkeychain.Extend return lp.rootKeyPriv.Derive(idx) } -func (lp *LockerParticipant) GetRootPrivateKey() string { - return lp.rootKeyPriv.String() +func (lp *LockerParticipant) GetRootPrivateKey() *hdkeychain.ExtendedKey { + return lp.rootKeyPriv } func (lp *LockerParticipant) IsRecordOwner(routingKey string, idx uint32) (*btcec.PublicKey, *AESKey, error) { diff --git a/model/locker_test.go b/model/locker_test.go index bc6f1cc..12b5359 100644 --- a/model/locker_test.go +++ b/model/locker_test.go @@ -99,7 +99,7 @@ func TestGenerateLocker(t *testing.T) { signKey := did1.SignKeyValue() keyBytes, err := AnonDecrypt(b, signKey) require.NoError(t, err) - assert.Equal(t, locker.Participants[0].GetRootPrivateKey(), string(keyBytes)) + assert.Equal(t, locker.Participants[0].GetRootPrivateKey().String(), string(keyBytes)) assert.Equal(t, did2.ID, locker.Participants[1].ID) assert.False(t, locker.Participants[1].Self) diff --git a/model/record.go b/model/record.go index 16318b1..af1f51b 100644 --- a/model/record.go +++ b/model/record.go @@ -17,6 +17,7 @@ package model import ( "bytes" "crypto/rand" + "crypto/sha256" "encoding/base64" "encoding/csv" "errors" @@ -27,6 +28,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/btcutil/base58" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/piprate/metalocker/utils" "github.com/piprate/metalocker/utils/jsonw" "github.com/rs/zerolog/log" @@ -254,6 +256,7 @@ func (r *Record) Validate() error { switch r.Operation { case OpTypeLease: case OpTypeLeaseRevocation: + // FIXME case OpTypeAssetHead: if r.SubjectRecord != "" { if len(r.RevocationProof) != 1 { @@ -298,3 +301,154 @@ func RandomKeyIndex() uint32 { return idx & 0x7fffffff // should be less than 0x80000000 to generate a non-hardened key } + +func BuildLeaseRecord(keyIndex uint32, recPrivKey *hdkeychain.ExtendedKey, lease *Lease, leaseAddress string, cleartext bool) (*Record, error) { + recordPubKey, err := recPrivKey.ECPubKey() + if err != nil { + return nil, err + } + + // generate authorising commitment + + ac := sha256.Sum256( + BuildAuthorisingCommitmentInput(recPrivKey, leaseAddress), + ) + + // generate requesting commitment + + rc := sha256.Sum256( + BuildRequestingCommitmentInput(lease.ID, lease.ExpiresAt), + ) + + // generate new record routing key + + routingKey, _ := BuildRoutingKey(recordPubKey) + + rec := &Record{ + RoutingKey: routingKey, + KeyIndex: keyIndex, + Operation: OpTypeLease, + OperationAddress: leaseAddress, + AuthorisingCommitment: base64.StdEncoding.EncodeToString(ac[:]), + AuthorisingCommitmentType: 0, + RequestingCommitment: base64.StdEncoding.EncodeToString(rc[:]), + RequestingCommitmentType: RcTypeAlgo1, + DataAssets: lease.GetResourceIDs(), + } + + if cleartext { + rec.Flags |= RecordFlagPublic + } + + // seal the record + + pk, err := recPrivKey.ECPrivKey() + if err != nil { + return nil, err + } + err = rec.Seal(pk) + if err != nil { + return nil, err + } + + return rec, nil +} + +func BuildAssetHeadRecord(senderID string, privateHDKey *hdkeychain.ExtendedKey, lockerID, sharedSecret, assetID, headName, recordID string, previousHeadRecord *Record) (*Record, error) { + + headID := HeadID(assetID, lockerID, sharedSecret, headName) + + var prevHeadRecordID string + var prevHeadRevocationProof []string + if previousHeadRecord != nil { + if previousHeadRecord.Status == StatusRevoked { + // Timing issues... + return nil, fmt.Errorf("asset head record already revoked: %s", previousHeadRecord.ID) + } + + prevHeadPrivKey, err := privateHDKey.Derive(previousHeadRecord.KeyIndex) + if err != nil { + return nil, err + } + + acInput := BuildAuthorisingCommitmentInput(prevHeadPrivKey, previousHeadRecord.OperationAddress) + subjAC := sha256.Sum256(acInput) + + if previousHeadRecord.AuthorisingCommitment != base64.StdEncoding.EncodeToString(subjAC[:]) { + return nil, errors.New( + "authorising commitment check failed. You are not authorised to update the asset head") + } + + prevHeadRecordID = previousHeadRecord.ID + prevHeadRevocationProof = []string{ + base64.StdEncoding.EncodeToString(acInput), + } + } + + keyIndex := RandomKeyIndex() + + recordPrivKey, err := privateHDKey.Derive(keyIndex) + if err != nil { + return nil, err + } + recordPubKey, err := recordPrivKey.ECPubKey() + if err != nil { + return nil, err + } + + // assetID, lockerID, participantID, name, recordID string + headBodyBytes := PackHeadBody(assetID, lockerID, senderID, headName, recordID) + + // derive symmetrical key + + sharedSecretBytes, err := base64.StdEncoding.DecodeString(sharedSecret) + if err != nil { + return nil, err + } + symKey := DeriveSymmetricalKey(sharedSecretBytes, recordPubKey) + + // encrypt head body + + encryptedHeadBody, err := EncryptAESCGM(headBodyBytes, symKey) + if err != nil { + return nil, err + } + + // generate authorising commitment + + ac := sha256.Sum256( + BuildAuthorisingCommitmentInput(recordPrivKey, ""), + ) + + // generate new record routing key + + routingKey, _ := BuildRoutingKey(recordPubKey) + + rec := &Record{ + RoutingKey: routingKey, + KeyIndex: keyIndex, + Operation: OpTypeAssetHead, + + AuthorisingCommitment: base64.StdEncoding.EncodeToString(ac[:]), + AuthorisingCommitmentType: 0, + + SubjectRecord: prevHeadRecordID, + RevocationProof: prevHeadRevocationProof, + + HeadID: headID, + HeadBody: base64.StdEncoding.EncodeToString(encryptedHeadBody), + } + + // seal the record + + pk, err := recordPrivKey.ECPrivKey() + if err != nil { + return nil, err + } + err = rec.Seal(pk) + if err != nil { + return nil, err + } + + return rec, nil +} diff --git a/model/scanner/scanner_test.go b/model/scanner/scanner_test.go index 37e42ee..c225fe9 100644 --- a/model/scanner/scanner_test.go +++ b/model/scanner/scanner_test.go @@ -35,7 +35,7 @@ import ( func TestScanner_Scan(t *testing.T) { env := testbase.SetUpTestEnvironment(t) - defer env.Close() + defer func() { _ = env.Close() }() // set up Data Wallet 1 @@ -149,7 +149,7 @@ func TestScanner_Scan(t *testing.T) { func TestScanner_Scan_TwoIterations(t *testing.T) { env := testbase.SetUpTestEnvironment(t) - defer env.Close() + defer func() { _ = env.Close() }() // set up Data Wallet 1 @@ -309,7 +309,7 @@ func TestScanner_Scan_TwoIterations(t *testing.T) { func TestScanner_RemoveSubscription(t *testing.T) { env := testbase.SetUpTestEnvironment(t) - defer env.Close() + defer func() { _ = env.Close() }() // set up Data Wallet 1 diff --git a/node/api/access_key_test.go b/node/api/access_key_test.go index 8088cd5..43ef70d 100644 --- a/node/api/access_key_test.go +++ b/node/api/access_key_test.go @@ -139,7 +139,7 @@ func TestAccountHandler_PostAccessKeyHandler(t *testing.T) { rec = invoke(acct.ID, acct.ID, bytes.NewReader(bodyBytes)) require.Equal(t, http.StatusCreated, rec.Code) - assert.Equal(t, "/test-url/"+ak.ID, rec.Result().Header.Get("Location")) //nolint:bodyclose + assert.Equal(t, "/test-url/"+ak.ID, rec.Result().Header.Get("Location")) var rsp map[string]any readBody(t, rec, &rsp) diff --git a/node/api/accounts_test.go b/node/api/accounts_test.go index 39c3a23..beea227 100644 --- a/node/api/accounts_test.go +++ b/node/api/accounts_test.go @@ -394,7 +394,7 @@ func createTestAccount(t *testing.T, email string, accessLevel model.AccessLevel func readBody(t *testing.T, rec *httptest.ResponseRecorder, dest any) { t.Helper() - rspBytes, err := io.ReadAll(rec.Result().Body) //nolint:bodyclose + rspBytes, err := io.ReadAll(rec.Result().Body) require.NoError(t, err) require.NoError(t, jsonw.Unmarshal(rspBytes, &dest)) diff --git a/node/api/identity_test.go b/node/api/identity_test.go index a52a833..99d4765 100644 --- a/node/api/identity_test.go +++ b/node/api/identity_test.go @@ -119,7 +119,7 @@ func TestPostIdentityHandler(t *testing.T) { rec = invoke(acct.ID, acct.ID, bytes.NewReader(bodyBytes)) require.Equal(t, http.StatusCreated, rec.Code) - assert.Equal(t, "/test-url/abc", rec.Result().Header.Get("Location")) //nolint:bodyclose + assert.Equal(t, "/test-url/abc", rec.Result().Header.Get("Location")) var rsp map[string]string readBody(t, rec, &rsp) diff --git a/node/api/locker_test.go b/node/api/locker_test.go index f0e2d4d..f8091db 100644 --- a/node/api/locker_test.go +++ b/node/api/locker_test.go @@ -119,7 +119,7 @@ func TestPostLockerHandler(t *testing.T) { rec = invoke(acct.ID, acct.ID, bytes.NewReader(bodyBytes)) require.Equal(t, http.StatusCreated, rec.Code) - assert.Equal(t, "/test-url/abc", rec.Result().Header.Get("Location")) //nolint:bodyclose + assert.Equal(t, "/test-url/abc", rec.Result().Header.Get("Location")) var rsp map[string]string readBody(t, rec, &rsp) diff --git a/node/api/property_test.go b/node/api/property_test.go index 4c1cf07..eff78a2 100644 --- a/node/api/property_test.go +++ b/node/api/property_test.go @@ -126,7 +126,7 @@ func TestAccountHandler_PostPropertyHandler(t *testing.T) { rec = invoke(acct.ID, acct.ID, bytes.NewReader(bodyBytes)) require.Equal(t, http.StatusCreated, rec.Code) - assert.Equal(t, "/test-url/abc", rec.Result().Header.Get("Location")) //nolint:bodyclose + assert.Equal(t, "/test-url/abc", rec.Result().Header.Get("Location")) var rsp map[string]string readBody(t, rec, &rsp) diff --git a/sdk/testbase/environment.go b/sdk/testbase/environment.go index 41f9911..8cfe636 100644 --- a/sdk/testbase/environment.go +++ b/sdk/testbase/environment.go @@ -20,12 +20,15 @@ import ( "os" "path/filepath" "testing" + "time" "github.com/gin-gonic/gin" "github.com/piprate/metalocker/contexts" "github.com/piprate/metalocker/index" "github.com/piprate/metalocker/index/bolt" "github.com/piprate/metalocker/ledger/local" + "github.com/piprate/metalocker/ledger/onflow" + "github.com/piprate/metalocker/ledger/onflow/emulator" "github.com/piprate/metalocker/model" "github.com/piprate/metalocker/model/account" "github.com/piprate/metalocker/node" @@ -194,12 +197,52 @@ func SetUpTestEnvironment(t *testing.T) *TestMetaLockerEnvironment { env.IdentityBackend, _ = memory.CreateIdentityBackend(nil, nil) - dbFilepath := filepath.Join(dir, "ledger.bolt") - env.NS = notification.NewLocalNotificationService(100) - ledgerAPI, err := local.NewBoltLedger(env.Ctx, dbFilepath, env.NS, 10, 0) - require.NoError(t, err) + var ledgerAPI model.Ledger + ledgerType := os.Getenv("LEDGER_TYPE") + if ledgerType == "" { + ledgerType = "local" + } + switch ledgerType { + case "onflow": + flowConnector, err := onflow.NewInMemoryConnectorEmbedded(true) + require.NoError(t, err) + flowConnector.DoNotPrependNetworkToAccountNames() + + emulator.ConfigureInMemoryEmulator(t, flowConnector, "emulator-metalocker-admin", "1000.0") + + dbFilepath := filepath.Join(dir, "db.bolt") + + se, err := onflow.NewTemplateEngine(flowConnector) + require.NoError(t, err) + + nodeAcct := flowConnector.Account("emulator-metalocker-platform") + emulator.FundAccountWithFlow(t, se, nodeAcct.Address, "10.0") + + workerCount := 1 + emulator.AddLedgerPoolKeys(t, flowConnector, "emulator-metalocker-platform", 0, workerCount) + + keyIndexes := make([]uint32, workerCount) + for i := 0; i < workerCount; i++ { + keyIndexes[i] = uint32(i + 1) + } + + ledger, err := onflow.NewLedger(context.Background(), flowConnector, "emulator", nodeAcct, keyIndexes, dbFilepath, env.NS) + require.NoError(t, err) + + require.NoError(t, ledger.StartLedgerEvents(env.Ctx, true, time.Second)) + + //require.NoError(t, ledger.Sync(env.Ctx)) + + ledgerAPI = ledger + case "local": + dbFilepath := filepath.Join(dir, "ledger.bolt") + ledgerAPI, err = local.NewBoltLedger(env.Ctx, dbFilepath, env.NS, 10, 0) + require.NoError(t, err) + default: + t.Fatalf("unknown ledger type: %s", ledgerType) + } env.BlobManager = TestBlobManager(t, false, ledgerAPI) diff --git a/utils/bolt.go b/utils/bolt.go index 3da0838..f144f25 100644 --- a/utils/bolt.go +++ b/utils/bolt.go @@ -84,6 +84,18 @@ func (bc *BoltClient) FetchInt(bucket, key string) (int, error) { } } +func (bc *BoltClient) FetchUint64(bucket, key string) (uint64, error) { + v, err := bc.FetchString(bucket, key) + if err != nil { + return 0, err + } + if v == "" { + return 0, nil + } else { + return strconv.ParseUint(v, 10, 64) + } +} + func (bc *BoltClient) Update(bucket, key string, value []byte) error { return bc.DB.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(bucket)) @@ -115,3 +127,7 @@ func (bc *BoltClient) UpdateInline(tx *bbolt.Tx, bucket, key string, value []byt func (bc *BoltClient) UpdateInt64(bucket, key string, value int64) error { return bc.Update(bucket, key, []byte(strconv.FormatInt(value, 10))) } + +func (bc *BoltClient) UpdateUint64(bucket, key string, value uint64) error { + return bc.Update(bucket, key, []byte(strconv.FormatUint(value, 10))) +} diff --git a/wallet/datastore.go b/wallet/datastore.go index ecc2ef1..12b534f 100644 --- a/wallet/datastore.go +++ b/wallet/datastore.go @@ -254,7 +254,7 @@ func (c *localStoreImpl) Submit(ctx context.Context, lease *model.Lease, clearte assetID := lease.Impression.Asset for _, name := range headName { - headID := model.HeadID(assetID, lockerID, sender, name) + headID := model.HeadID(assetID, lockerID, sender.SharedSecret, name) headRecID, err := c.submitHead(ctx, assetID, lockerID, sender, name, rec.ID) if err != nil { return dataset.RecordFutureWithError(err) @@ -271,11 +271,11 @@ func (c *localStoreImpl) Submit(ctx context.Context, lease *model.Lease, clearte func (c *localStoreImpl) submitLease(ctx context.Context, lease *model.Lease, cleartext bool, p *model.LockerParticipant) (*model.Record, error) { keyIndex := model.RandomKeyIndex() - recordPrivKey, err := p.GetRecordPrivateKey(keyIndex) + recPrivKey, err := p.GetRootPrivateKey().Derive(keyIndex) if err != nil { return nil, err } - recordPubKey, err := recordPrivKey.ECPubKey() + recordPubKey, err := recPrivKey.ECPubKey() if err != nil { return nil, err } @@ -307,48 +307,7 @@ func (c *localStoreImpl) submitLease(ctx context.Context, lease *model.Lease, cl return nil, err } - // generate authorising commitment - - ac := sha256.Sum256( - model.BuildAuthorisingCommitmentInput(recordPrivKey, leaseAddress), - ) - - // generate requesting commitment - - rc := sha256.Sum256( - model.BuildRequestingCommitmentInput( - lease.ID, - lease.ExpiresAt, - ), - ) - - // generate new record routing key - - routingKey, _ := model.BuildRoutingKey(recordPubKey) - - rec := &model.Record{ - RoutingKey: routingKey, - KeyIndex: keyIndex, - Operation: model.OpTypeLease, - OperationAddress: leaseAddress, - AuthorisingCommitment: base64.StdEncoding.EncodeToString(ac[:]), - AuthorisingCommitmentType: 0, - RequestingCommitment: base64.StdEncoding.EncodeToString(rc[:]), - RequestingCommitmentType: model.RcTypeAlgo1, - DataAssets: lease.GetResourceIDs(), - } - - if cleartext { - rec.Flags |= model.RecordFlagPublic - } - - // seal the record - - pk, err := recordPrivKey.ECPrivKey() - if err != nil { - return nil, err - } - err = rec.Seal(pk) + rec, err := model.BuildLeaseRecord(keyIndex, recPrivKey, lease, leaseAddress, cleartext) if err != nil { return nil, err } @@ -642,101 +601,14 @@ func (c *localStoreImpl) SetAssetHead(ctx context.Context, assetID string, locke func (c *localStoreImpl) submitHead(ctx context.Context, assetID, lockerID string, sender *model.LockerParticipant, headName, recordID string) (string, error) { - headID := model.HeadID(assetID, lockerID, sender, headName) + headID := model.HeadID(assetID, lockerID, sender.SharedSecret, headName) prevHead, err := c.ledger.GetAssetHead(ctx, headID) if err != nil && !errors.Is(err, model.ErrAssetHeadNotFound) { return "", err } - var prevHeadRecordID string - var prevHeadRevocationProof []string - if prevHead != nil { - if prevHead.Status == model.StatusRevoked { - // Timing issues... - return "", fmt.Errorf("asset head record already revoked: %s", prevHead.ID) - } - - prevHeadPrivKey, err := sender.GetRecordPrivateKey(prevHead.KeyIndex) - if err != nil { - return "", err - } - - acInput := model.BuildAuthorisingCommitmentInput(prevHeadPrivKey, prevHead.OperationAddress) - subjAC := sha256.Sum256(acInput) - - if prevHead.AuthorisingCommitment != base64.StdEncoding.EncodeToString(subjAC[:]) { - return "", errors.New( - "authorising commitment check failed. You are not authorised to update the asset head") - } - - prevHeadRecordID = prevHead.ID - prevHeadRevocationProof = []string{ - base64.StdEncoding.EncodeToString(acInput), - } - } - - keyIndex := model.RandomKeyIndex() - - recordPrivKey, err := sender.GetRecordPrivateKey(keyIndex) - if err != nil { - return "", err - } - recordPubKey, err := recordPrivKey.ECPubKey() - if err != nil { - return "", err - } - - // assetID, lockerID, participantID, name, recordID string - headBodyBytes := model.PackHeadBody(assetID, lockerID, sender.ID, headName, recordID) - - // derive symmetrical key - - sharedSecretBytes, err := base64.StdEncoding.DecodeString(sender.SharedSecret) - if err != nil { - return "", err - } - symKey := model.DeriveSymmetricalKey(sharedSecretBytes, recordPubKey) - - // encrypt head body - - encryptedHeadBody, err := model.EncryptAESCGM(headBodyBytes, symKey) - if err != nil { - return "", err - } - - // generate authorising commitment - - ac := sha256.Sum256( - model.BuildAuthorisingCommitmentInput(recordPrivKey, ""), - ) - - // generate new record routing key - - routingKey, _ := model.BuildRoutingKey(recordPubKey) - - rec := &model.Record{ - RoutingKey: routingKey, - KeyIndex: keyIndex, - Operation: model.OpTypeAssetHead, - - AuthorisingCommitment: base64.StdEncoding.EncodeToString(ac[:]), - AuthorisingCommitmentType: 0, - - SubjectRecord: prevHeadRecordID, - RevocationProof: prevHeadRevocationProof, - - HeadID: headID, - HeadBody: base64.StdEncoding.EncodeToString(encryptedHeadBody), - } - - // seal the record - - pk, err := recordPrivKey.ECPrivKey() - if err != nil { - return "", err - } - err = rec.Seal(pk) + rec, err := model.BuildAssetHeadRecord(sender.ID, sender.GetRootPrivateKey(), lockerID, sender.SharedSecret, assetID, headName, recordID, prevHead) if err != nil { return "", err } diff --git a/wallet/locker.go b/wallet/locker.go index ea3b602..a822c49 100644 --- a/wallet/locker.go +++ b/wallet/locker.go @@ -155,7 +155,7 @@ func (lw *lockerWrapper) Share(ctx context.Context, id, vaultName string, expiry } func (lw *lockerWrapper) HeadID(ctx context.Context, assetID string, headName string) string { - return model.HeadID(assetID, lw.raw.ID, lw.raw.Us(), headName) + return model.HeadID(assetID, lw.raw.ID, lw.raw.Us().SharedSecret, headName) } func (lw *lockerWrapper) SetAssetHead(ctx context.Context, assetID, headName, recordID string) dataset.RecordFuture { From f0e7e351c7b6f434f915a292f337773572723608 Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 23 Mar 2025 14:42:39 +0000 Subject: [PATCH 3/9] Support for importing ledger data + contract fixes --- .gitignore | 3 + cmd/lockerd/modules.go | 1 + cmd/metalo/actions/admin.go | 23 +++ cmd/metalo/actions/definitions.go | 9 + cmd/metalo/actions/ledger.go | 27 --- cmd/metalo/modules.go | 3 + cmd/metalo/operations/export_ledger.go | 2 +- cmd/metalo/operations/import_ledger.go | 173 ++++++++++++++++-- index/bolt/store.go | 13 +- index/interface.go | 1 + ledger/local/ledger.go | 76 +++++++- ledger/onflow/common_test.go | 8 +- ledger/onflow/contracts/MetaLocker.cdc | 19 +- ledger/onflow/ledger.go | 113 ++++++++++-- ledger/onflow/ledger_test.go | 75 ++++++++ .../metalocker_get_data_asset_counter.cdc | 2 +- .../transactions/metalocker_import_block.cdc | 9 + model/access_token.go | 4 + model/ledger.go | 4 + remote/caller/blocks.go | 5 + sdk/testbase/environment.go | 4 +- 21 files changed, 498 insertions(+), 76 deletions(-) create mode 100644 ledger/onflow/templates/transactions/metalocker_import_block.cdc diff --git a/.gitignore b/.gitignore index 20729f8..03761a8 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ vendor /coverage.txt dist/ + +# local Flow emulator files +/ledger/onflow/flowdb diff --git a/cmd/lockerd/modules.go b/cmd/lockerd/modules.go index f57c81b..c33ae4e 100644 --- a/cmd/lockerd/modules.go +++ b/cmd/lockerd/modules.go @@ -22,6 +22,7 @@ import ( _ "github.com/piprate/metalocker/index/bolt" _ "github.com/piprate/metalocker/ledger/local" + _ "github.com/piprate/metalocker/ledger/onflow" _ "github.com/piprate/metalocker/storage/memory" diff --git a/cmd/metalo/actions/admin.go b/cmd/metalo/actions/admin.go index 89ef78c..8a9b369 100644 --- a/cmd/metalo/actions/admin.go +++ b/cmd/metalo/actions/admin.go @@ -105,6 +105,29 @@ func ImportBackendData(c *cli.Context) error { return nil } +func ImportLedger(c *cli.Context) error { + if c.Args().Len() != 1 { + return cli.Exit("please specify the path to data folder", InvalidParameter) + } + folderPath := utils.AbsPathify(c.Args().Get(0)) + + configFilePath := c.String("config") + if configFilePath == "" { + return cli.Exit("please specify the path to MetaLocker configuration file using --config flag", InvalidParameter) + } + + importOperations := c.Bool("import-operations") + preserveBlocks := c.Bool("preserve-blocks") + waitForConfirmation := c.Bool("wait") + + err := operations.ImportLedger(c.Context, folderPath, configFilePath, importOperations, preserveBlocks, waitForConfirmation) + if err != nil { + log.Err(err).Msg("Ledger import failed") + return cli.Exit(err, OperationFailed) + } + return nil +} + func UpdateAccountState(c *cli.Context) error { if c.Args().Len() != 1 { fmt.Print("Please specify the account ID (email or DID) to block.\n\n") diff --git a/cmd/metalo/actions/definitions.go b/cmd/metalo/actions/definitions.go index 035138a..4e80900 100644 --- a/cmd/metalo/actions/definitions.go +++ b/cmd/metalo/actions/definitions.go @@ -642,10 +642,19 @@ var ( Usage: "import MetaLocker ledger data from the given directory", Action: ImportLedger, Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Value: "", + Usage: "path to MetaLocker configuration file", + }, &cli.BoolFlag{ Name: "import-operations", Usage: "import operations (be careful if swapping ledgers!)", }, + &cli.BoolFlag{ + Name: "preserve-blocks", + Usage: "import blocks while preserving their composition and numbering (if possible)", + }, &cli.BoolFlag{ Name: "wait", Usage: "If specified, wait until each block is published on the ledger", diff --git a/cmd/metalo/actions/ledger.go b/cmd/metalo/actions/ledger.go index 6af0278..8708717 100644 --- a/cmd/metalo/actions/ledger.go +++ b/cmd/metalo/actions/ledger.go @@ -40,30 +40,3 @@ func ExportLedger(c *cli.Context) error { } return err } - -func ImportLedger(c *cli.Context) error { - if c.Args().Len() != 1 { - fmt.Print("Please specify the path to file or folder.\n\n") - return cli.Exit("please specify the path to file or folder", InvalidParameter) - } - - importOperations := c.Bool("import-operations") - waitForConfirmation := c.Bool("wait") - - dw, err := LoadRemoteDataWallet(c, false) - if err != nil { - return err - } - - ns, err := dw.Services().NotificationService() - if err != nil { - return err - } - - err = operations.ImportLedger(c.Context, dw.Services().Ledger(), dw.Services().OffChainStorage(), ns, c.Args().Get(0), importOperations, waitForConfirmation) - if err != nil { - log.Err(err).Msg("Ledger import failed") - return cli.Exit(err, OperationFailed) - } - return nil -} diff --git a/cmd/metalo/modules.go b/cmd/metalo/modules.go index 4e72dc1..c536f39 100644 --- a/cmd/metalo/modules.go +++ b/cmd/metalo/modules.go @@ -22,6 +22,9 @@ import ( _ "github.com/piprate/metalocker/storage/memory" _ "github.com/piprate/metalocker/storage/rdb" + + _ "github.com/piprate/metalocker/ledger/local" + _ "github.com/piprate/metalocker/ledger/onflow" ) func init() { diff --git a/cmd/metalo/operations/export_ledger.go b/cmd/metalo/operations/export_ledger.go index 7cf959e..96a47cc 100644 --- a/cmd/metalo/operations/export_ledger.go +++ b/cmd/metalo/operations/export_ledger.go @@ -124,7 +124,7 @@ func SaveBlock(ctx context.Context, ledger model.Ledger, offChainStorage model.O recs = append(recs, rec) - if rec.Operation == model.OpTypeLease { + if rec.Operation == model.OpTypeLease && rec.Status == model.StatusPublished { opRecBytes, err := offChainStorage.GetOperation(ctx, rec.OperationAddress) if err != nil { return err diff --git a/cmd/metalo/operations/import_ledger.go b/cmd/metalo/operations/import_ledger.go index f8dec51..24c719e 100644 --- a/cmd/metalo/operations/import_ledger.go +++ b/cmd/metalo/operations/import_ledger.go @@ -15,23 +15,53 @@ package operations import ( + "bytes" "context" + "fmt" "os" "path" "path/filepath" "strings" "time" + "github.com/knadh/koanf" + "github.com/piprate/metalocker/ledger" "github.com/piprate/metalocker/model" "github.com/piprate/metalocker/model/dataset" - "github.com/piprate/metalocker/services/notification" + "github.com/piprate/metalocker/sdk/cmdbase" "github.com/piprate/metalocker/utils" "github.com/piprate/metalocker/utils/jsonw" + "github.com/piprate/metalocker/vaults" "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" ) -func ImportLedger(ctx context.Context, ledger model.Ledger, offChainStorage model.OffChainStorage, ns notification.Service, dest string, importOperations, waitForConfirmation bool) error { - tbBytes, err := os.ReadFile(path.Join(dest, "_top.json")) +func ImportLedger(ctx context.Context, importDirPath, configFilePath string, importOperations, preserveBlocks, waitForConfirmation bool) error { + // read configuration + cfg, err := readConfigFile(configFilePath) + if err != nil { + return err + } + + resolver, err := cmdbase.ConfigureParameterResolver(cfg, filepath.Dir(configFilePath)) + if err != nil { + return err + } + + destLedger, err := initLedgerInstance(cfg, resolver) + if err != nil { + return err + } + + var offChainVault vaults.Vault + if importOperations { + offChainVault, err = initOffChainVault(cfg, resolver) + if err != nil { + return err + } + } + + tbBytes, err := os.ReadFile(path.Join(importDirPath, "_top.json")) if err != nil { return err } @@ -42,12 +72,24 @@ func ImportLedger(ctx context.Context, ledger model.Ledger, offChainStorage mode return err } - var i uint64 - for i = 0; i <= tb.Number; i++ { - blockPath := path.Join(dest, utils.Uint64ToString(i)) + var topDestBlockNumber uint64 + if preserveBlocks { + top, err := destLedger.GetTopBlock(ctx) + if err != nil { + return err + } + if top != nil { + topDestBlockNumber = top.Number + } + } + var blockNumber uint64 + var blockOneRecords []*model.Record + importedRecords := make(map[string]bool) + for ; blockNumber <= tb.Number; blockNumber++ { + blockPath := path.Join(importDirPath, utils.Uint64ToString(blockNumber)) if importOperations { - if err := filepath.Walk(path.Join(blockPath, "operations"), func(filePath string, f os.FileInfo, err error) error { + if err = filepath.Walk(path.Join(blockPath, "operations"), func(filePath string, f os.FileInfo, err error) error { if err != nil { return err } @@ -63,12 +105,12 @@ func ImportLedger(ctx context.Context, ledger model.Ledger, offChainStorage mode return err } - opid, err := offChainStorage.SendOperation(ctx, opBytes) + opRec, err := offChainVault.CreateBlob(ctx, bytes.NewReader(opBytes)) if err != nil { return err } - log.Info().Str("file", filePath).Str("id", opid).Msg("Saved operation") + log.Info().Str("file", filePath).Str("id", opRec.ID).Msg("Saved operation") return nil }); err != nil { @@ -87,23 +129,118 @@ func ImportLedger(ctx context.Context, ledger model.Ledger, offChainStorage mode return err } - var r *model.Record - for _, r = range recs { - log.Info().Str("rid", r.ID).Msg("Importing record") + log.Info().Uint64("number", blockNumber).Int("record_count", len(recs)).Msg("Importing block") - if err = ledger.SubmitRecord(ctx, r); err != nil { - return err + // remove duplicate records + + cleanRecs := make([]*model.Record, 0, len(recs)) + for _, rec := range recs { + if _, duplicate := importedRecords[rec.ID]; duplicate { + log.Warn().Uint64("number", blockNumber).Str("rid", rec.ID).Msg("Duplicate record found, skipping...") + continue + } else { + cleanRecs = append(cleanRecs, rec) + importedRecords[rec.ID] = true } } + recs = cleanRecs + + if preserveBlocks { + if blockNumber <= topDestBlockNumber { + // incremental mode. Check that source and destination blocks are identical + destRecList, err := destLedger.GetBlockRecords(ctx, blockNumber) + if err != nil { + return err + } + if len(destRecList) != len(recs) { + if blockNumber == 1 { + blockOneRecords = recs + log.Warn().Int("record_count", len(recs)).Msg("Found records in block 1. Adding to block 2") + continue + } else if blockNumber == 2 && len(destRecList) > len(recs) { + log.Warn().Int("src_record_count", len(recs)).Int("dest_record_count", len(destRecList)).Msg("Extra records found in block 2. Assuming they were moved from block 1") + continue + } else { + return fmt.Errorf("block already exists: %d. Got %d record(s) in destination ledger, got %d source record(s)", blockNumber, len(destRecList), len(recs)) + } + } + } else { + if blockNumber == 2 && len(blockOneRecords) > 0 { + log.Warn().Int("b2_record_count", len(recs)).Int("b1_record_count", len(blockOneRecords)).Msg("Importing block 2 with extra records from block 1") + recs = append(recs, blockOneRecords...) + blockOneRecords = nil + } + if err = destLedger.ImportBlock(ctx, blockNumber, recs); err != nil { + return err + } + } + } else { + var r *model.Record + for _, r = range recs { + log.Info().Str("rid", r.ID).Msg("Importing record") - if r != nil && waitForConfirmation { - // wait for the last record in the block + if r.Status == model.StatusRevoked { + r.Status = model.StatusPublished + } - if _, err = dataset.WaitForConfirmation(ctx, ledger, nil, time.Second, 60*time.Second, r.ID); err != nil { - return err + if err = destLedger.SubmitRecord(ctx, r); err != nil { + return err + } + } + + if r != nil && waitForConfirmation { + // wait for the last record in the block + + if _, err = dataset.WaitForConfirmation(ctx, destLedger, nil, time.Second, 60*time.Second, r.ID); err != nil { + return err + } } } } return nil } + +func initLedgerInstance(cfg *koanf.Koanf, resolver cmdbase.ParameterResolver) (model.Ledger, error) { + if cfg.Exists("ledger") { + var ledgerCfg *ledger.Config + + err := cfg.Unmarshal("ledger", &ledgerCfg) + if err != nil { + log.Err(err).Msg("Failed to read ledger configuration") + return nil, cli.Exit(err, 1) + } + + identityBackend, err := ledger.CreateLedgerConnector(context.Background(), ledgerCfg, nil, resolver) + if err != nil { + log.Err(err).Msg("Failed to create ledger instance") + return nil, cli.Exit(err, 1) + } + + return identityBackend, nil + } else { + return nil, cli.Exit("ledger not defined", 1) + } +} + +func initOffChainVault(cfg *koanf.Koanf, resolver cmdbase.ParameterResolver) (vaults.Vault, error) { + var vaultCfg vaults.Config + err := cfg.Unmarshal("offChainStore", &vaultCfg) + if err != nil { + log.Err(err).Msg("Failed to read vault configuration") + return nil, cli.Exit(err, 1) + } + + offchainAPI, err := vaults.CreateVault(&vaultCfg, resolver, nil) + if err != nil { + log.Err(err).Msg("Failed to create an offchain vault") + os.Exit(1) + } + + if !offchainAPI.CAS() { + log.Err(err).Msg("Offchain operation vault should be a content-addressable storage") + return nil, cli.Exit(err, 1) + } + + return offchainAPI, nil +} diff --git a/index/bolt/store.go b/index/bolt/store.go index e2d02e4..e735fe1 100644 --- a/index/bolt/store.go +++ b/index/bolt/store.go @@ -205,9 +205,16 @@ func (s *IndexStore) Bind(ctx context.Context, gbHash string) error { return err } } else if walletBlockHash != gbHash { - return fmt.Errorf( - "genesis block hash mismatch between MetaLocker and local data wallet index: %s != %s", - gbHash, walletBlockHash) + if s.cfg.Rebind { + log.Warn().Str("old_hash", walletBlockHash).Str("new_hash", gbHash).Msg("Binding Bolt index to new genesis block") + if err = s.storeGenesisBlock(gbHash); err != nil { + return err + } + } else { + return fmt.Errorf( + "genesis block hash mismatch between MetaLocker and local data wallet index: %s != %s", + gbHash, walletBlockHash) + } } s.genesisBlockHash = gbHash diff --git a/index/interface.go b/index/interface.go index 6569e3c..92f392f 100644 --- a/index/interface.go +++ b/index/interface.go @@ -99,6 +99,7 @@ type ( Name string `koanf:"name" json:"name"` Type string `koanf:"type" json:"type"` EncryptionMode EncryptionMode `koanf:"encryptionMode" json:"encryptionMode"` + Rebind bool `koanf:"rebind" json:"rebind"` Params map[string]any `koanf:"params" json:"params,omitempty"` } diff --git a/ledger/local/ledger.go b/ledger/local/ledger.go index c63500c..bb32d56 100644 --- a/ledger/local/ledger.go +++ b/ledger/local/ledger.go @@ -152,7 +152,48 @@ func (bl *BoltLedger) SubmitRecord(ctx context.Context, r *model.Record) error { return err } - bl.records <- *r + rCopy := r.Copy() + + bl.records <- *rCopy + + return nil +} + +func (bl *BoltLedger) ImportBlock(ctx context.Context, blockNumber uint64, records []*model.Record) error { + if len(records) > bl.maxRecordsPerBlock { + return fmt.Errorf("too many records: trying to import more than %d records (%d)", bl.maxRecordsPerBlock, len(records)) + } + + // check the imported block has a correct number + + prevBlock, err := bl.GetTopBlock(ctx) + if err != nil && !errors.Is(err, model.ErrBlockNotFound) { + return err + } + if (prevBlock == nil && blockNumber != 0) || (prevBlock != nil && blockNumber != prevBlock.Number+1) { + var prevNumber uint64 + if prevBlock != nil { + prevNumber = prevBlock.Number + } + return fmt.Errorf("block number %d is out of range. Top block number = %d", blockNumber, prevNumber) + } + + errorCount := 0 + for _, r := range records { + if err = bl.SaveRecord(r); err != nil { + log.Err(err).Str("rid", r.ID).Msg("Failed to save ledger record") + errorCount++ + } + } + + if errorCount > 0 { + return fmt.Errorf("failed to import %d record(s)", errorCount) + } + + if err = generateNewBlock(ctx, bl, ""); err != nil { + log.Err(err).Msg("Failed to generate new block on import") + return err + } return nil } @@ -474,6 +515,28 @@ func (bl *BoltLedger) CurrentBlockSession() string { return v } +func (bl *BoltLedger) ClearPendingRecordsIfExist() error { + curSessionID, err := bl.client.FetchString(ControlsKey, CurrentSessionIDKey) + if err != nil { + return err + } + if curSessionID != "" { + return bl.client.DB.Update(func(tx *bbolt.Tx) error { + urBucket := tx.Bucket([]byte(UnconfirmedRecordsKey)) + + csb := urBucket.Bucket([]byte(curSessionID)) + if csb != nil { + return urBucket.DeleteBucket([]byte(curSessionID)) + } else { + // empty session + return nil + } + }) + } else { + return nil + } +} + func (bl *BoltLedger) OpenNewBlockSession() (string, error) { curSessionID := fmt.Sprintf("block%d", time.Now().Unix()) @@ -610,7 +673,7 @@ func (bl *BoltLedger) SubmitNewBlock(block *model.Block, records []*model.Record blockKey := utils.Uint64ToString(block.Number) - if err := bl.client.DB.Update(func(tx *bbolt.Tx) error { + if err = bl.client.DB.Update(func(tx *bbolt.Tx) error { if err = bl.client.UpdateInline(tx, BlocksKey, blockKey, bb); err != nil { return err } @@ -667,6 +730,7 @@ func (bl *BoltLedger) SubmitNewBlock(block *model.Block, records []*model.Record if err := bl.updateRecordState(tx, rec.ID, model.StatusFailed, 0); err != nil { log.Err(err).Str("rid", rec.ID).Msg("Error when setting record status as failed") } + log.Warn().AnErr("err", err).Str("rid", rec.ID).Msg("Error when applying revocations") continue } @@ -727,7 +791,7 @@ func (bl *BoltLedger) SubmitNewBlock(block *model.Block, records []*model.Record // Consider this block published - if err := bl.client.UpdateInline(tx, ControlsKey, TopBlockNumberKey, []byte(blockKey)); err != nil { + if err = bl.client.UpdateInline(tx, ControlsKey, TopBlockNumberKey, []byte(blockKey)); err != nil { return err } @@ -902,7 +966,7 @@ func generateNewBlock(ctx context.Context, bl *BoltLedger, seed string) error { Nonce: base64.StdEncoding.EncodeToString(nonce), } - if err := newLocalBlock.Seal(); err != nil { + if err = newLocalBlock.Seal(); err != nil { return err } @@ -917,6 +981,10 @@ func generateNewBlock(ctx context.Context, bl *BoltLedger, seed string) error { return err } + if err := bl.ClearPendingRecordsIfExist(); err != nil { + log.Err(err).Msg("Failed to clear pending records") + } + if _, err := bl.OpenNewBlockSession(); err != nil { return err } diff --git a/ledger/onflow/common_test.go b/ledger/onflow/common_test.go index c229247..cd2a0af 100644 --- a/ledger/onflow/common_test.go +++ b/ledger/onflow/common_test.go @@ -2,6 +2,7 @@ package onflow_test import ( "context" + "fmt" "strings" "testing" @@ -117,7 +118,12 @@ func AssertDataAssetCounter(t *testing.T, se *splash.TemplateEngine, id string, RunReturns(context.Background()) require.NoError(t, err) - assert.Equal(t, counter, uint64(val.(cadence.UInt64))) + opt, _ := val.(cadence.Optional) + if opt.Value == nil { + assert.Fail(t, fmt.Sprintf("data asset counter is nil")) + } else { + assert.Equal(t, counter, uint64(opt.Value.(cadence.UInt64))) + } } func GetAssetHead(t *testing.T, se *splash.TemplateEngine, headID string) *model.Record { diff --git a/ledger/onflow/contracts/MetaLocker.cdc b/ledger/onflow/contracts/MetaLocker.cdc index 1319830..1bad366 100644 --- a/ledger/onflow/contracts/MetaLocker.cdc +++ b/ledger/onflow/contracts/MetaLocker.cdc @@ -221,7 +221,7 @@ contract MetaLocker { access(contract) fun rollBlock(blockHeight: UInt64) { let nonce = revertibleRandom().toBigEndianBytes() - let currentRecordCount = UInt32(self.records.length) + let currentRecordCount = UInt32(self.records.length) // FIXME let base = self.prevBlockHash .concat(nonce) .concat(currentRecordCount.toBigEndianBytes()) @@ -335,7 +335,7 @@ contract MetaLocker { let decrementCounter = fun (dataAssetID: String) { var counter = self.dataAssetCounters[dataAssetID] - if counter != nil { + if counter != nil && counter! > 0 { self.dataAssetCounters[dataAssetID] = counter! - 1 } } @@ -398,18 +398,29 @@ contract MetaLocker { self.assetHeads[record.headID] = record.id } + access(all) + fun importBlock(number: UInt64, records: [Record]) { + pre { + self.currentBlockNumber == number-1 : "unexpected current block number" + } + self.rollBlock(blockHeight: getCurrentBlock().height) + for rec in records { + self.submitRecord(record: rec) + } + } + access(all) fun getRecord(id: String): Record? { return self.records[id] } access(all) - fun getDataAssetCounter(id: String): UInt64 { + fun getDataAssetCounter(id: String): UInt64? { let counter = self.dataAssetCounters[id] if counter != nil { return counter! } else { - return 0 + return nil } } diff --git a/ledger/onflow/ledger.go b/ledger/onflow/ledger.go index 44e1efb..6c3996b 100644 --- a/ledger/onflow/ledger.go +++ b/ledger/onflow/ledger.go @@ -574,15 +574,21 @@ func (l *Ledger) SubmitRecord(ctx context.Context, r *model.Record) error { l.dequeMutex.Unlock() defer l.keyDeque.PushBack(keyIndex) - acct := &accounts.Account{ - Name: "pool", - Address: l.nodeAccount.Address, - Key: accounts.NewHexKeyFromPrivateKey(keyIndex, crypto.SHA3_256, l.privateKey), + var acct *accounts.Account + if keyIndex != l.nodeAccount.Key.Index() { + acct = &accounts.Account{ + Name: "pool", + Address: l.nodeAccount.Address, + Key: accounts.NewHexKeyFromPrivateKey(keyIndex, crypto.SHA3_256, l.privateKey), + } + } else { + acct = l.nodeAccount } + cadenceRecord := RecordToCadence(r, l.scriptsEngine.ContractAddress("MetaLocker")) + for { - txBuilder := l.scriptsEngine.NewTransaction("metalocker_submit_record"). - Argument(RecordToCadence(r, l.scriptsEngine.ContractAddress("MetaLocker"))) + txBuilder := l.scriptsEngine.NewTransaction("metalocker_submit_record").Argument(cadenceRecord) txBuilder.Proposer = acct txBuilder.Payer = acct txBuilder.MainSigner = l.nodeAccount @@ -605,6 +611,62 @@ func (l *Ledger) SubmitRecord(ctx context.Context, r *model.Record) error { return err } +func (l *Ledger) ImportBlock(ctx context.Context, blockNumber uint64, records []*model.Record) error { + // check the imported block has a correct number + + prevBlock, err := l.GetTopBlock(ctx) + if err != nil && !errors.Is(err, model.ErrBlockNotFound) { + return err + } + if prevBlock != nil && blockNumber != prevBlock.Number+1 { + return fmt.Errorf("block number %d is out of range. Top block number = %d", blockNumber, prevBlock.Number) + } + + l.dequeMutex.Lock() + keyIndex := l.keyDeque.PopFront() + l.dequeMutex.Unlock() + defer l.keyDeque.PushBack(keyIndex) + + var acct *accounts.Account + if keyIndex != l.nodeAccount.Key.Index() { + acct = &accounts.Account{ + Name: "pool", + Address: l.nodeAccount.Address, + Key: accounts.NewHexKeyFromPrivateKey(keyIndex, crypto.SHA3_256, l.privateKey), + } + } else { + acct = l.nodeAccount + } + + mlAddress := l.scriptsEngine.ContractAddress("MetaLocker") + cadenceRecordList := make([]cadence.Value, len(records)) + for i, r := range records { + cadenceRecordList[i] = RecordToCadence(r, mlAddress) + } + cadenceRecords := cadence.NewArray(cadenceRecordList) + + for { + txBuilder := l.scriptsEngine.NewTransaction("metalocker_import_block"). + UInt64Argument(blockNumber).Argument(cadenceRecords) + txBuilder.Proposer = acct + txBuilder.Payer = acct + txBuilder.MainSigner = l.nodeAccount + + err = l.runTx(ctx, &txBuilder) + if err != nil { + if strings.HasPrefix(err.Error(), "transaction is expired") { + log.Warn().Err(err).Uint64("importedBlockNumber", blockNumber).Msg("EXPIRED") + continue + } + return err + } else { + break + } + } + + return nil +} + func (l *Ledger) GetRecord(ctx context.Context, rid string) (*model.Record, error) { defer measure.ExecTime("onflow.GetRecord")() @@ -750,7 +812,7 @@ func (l *Ledger) GetTopBlock(ctx context.Context) (*model.Block, error) { return &model.Block{ Number: topBlockNumber, - Hash: "", + Hash: "", // FIXME ParentHash: "", Status: 0, }, nil @@ -787,17 +849,23 @@ func (l *Ledger) GetChain(ctx context.Context, startNumber uint64, depth int) ([ } func (l *Ledger) GetDataAssetState(ctx context.Context, id string) (model.DataAssetState, error) { - val, err := l.scriptsEngine.NewScript("metalocker_get_data_asset_counter"). + optVal, err := l.scriptsEngine.NewScript("metalocker_get_data_asset_counter"). Argument(cadence.String(id)). RunReturns(ctx) if err != nil { return model.DataAssetStateNotFound, err } - counter := uint64(val.(cadence.UInt64)) - if counter > 0 { - return model.DataAssetStateKeep, nil + + val, _ := optVal.(cadence.Optional) + if val.Value == nil { + return model.DataAssetStateNotFound, nil } else { - return model.DataAssetStateRemove, nil + counter := uint64(val.Value.(cadence.UInt64)) + if counter > 0 { + return model.DataAssetStateKeep, nil + } else { + return model.DataAssetStateRemove, nil + } } } @@ -958,12 +1026,20 @@ func CreateLedgerConnector(ctx context.Context, params ledger.Parameters, ns not return nil, err } + if nodeAddress == "" { + return nil, errors.New("node address is required (use 'nodeAddress' connector property)") + } + nodeKey, err := resolver.ResolveString(params["nodeKey"]) if err != nil { return nil, err } - keyIndexStr, err := resolver.ResolveString(params["keyIndex"]) + if nodeKey == "" { + return nil, errors.New("node key is required (use 'nodeKey' connector property)") + } + + keyIndexStr, err := resolver.ResolveString(params["nodeKeyIndex"]) if err != nil { return nil, err } @@ -981,5 +1057,14 @@ func CreateLedgerConnector(ctx context.Context, params ledger.Parameters, ns not } bl, err := NewLedger(ctx, flowConnector, network, nodeAcct, []uint32{0}, utils.AbsPathify(dbFilePath), ns) - return bl, err + if err != nil { + return nil, err + } + + err = bl.StartLedgerEvents(ctx, true, 0) + if err != nil { + return nil, err + } + + return bl, nil } diff --git a/ledger/onflow/ledger_test.go b/ledger/onflow/ledger_test.go index 46a7e1d..b655c8d 100644 --- a/ledger/onflow/ledger_test.go +++ b/ledger/onflow/ledger_test.go @@ -399,5 +399,80 @@ func TestLedger_GetRecordState(t *testing.T) { } func TestLedger_GetAssetHead(t *testing.T) { + // TODO +} + +func TestLedger_GetDataAssetState(t *testing.T) { + fl, _, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + state, err := fl.GetDataAssetState(context.Background(), "non-existent-asset") + require.NoError(t, err) + + assert.Equal(t, model.DataAssetStateNotFound, state) +} + +func TestLedger_ImportBlock(t *testing.T) { + fl, _, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var template *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &template)) + ctx := context.Background() + + err := fl.ImportBlock(ctx, 1, nil) + require.Error(t, err) + assert.Equal(t, "block number 1 is out of range. Top block number = 1", err.Error()) + + recList := make([]*model.Record, 3) + for i := 0; i < 3; i++ { + rec := template.Copy() + rec.ID = fmt.Sprintf("record-%d", i+1) + recList[i] = rec + } + err = fl.ImportBlock(ctx, 2, recList) + require.NoError(t, err) + + require.NoError(t, fl.Sync(ctx)) + + for i := 0; i < 3; i++ { + recID := fmt.Sprintf("record-%d", i+1) + + state, err := fl.GetRecordState(ctx, recID) + require.NoError(t, err) + assert.Equal(t, model.StatusPublished, state.Status) + assert.Equal(t, uint64(2), state.BlockNumber) + } +} + +func TestLedger_ImportBlock_Empty(t *testing.T) { + fl, _, dir := NewTestLedger(t, 1) + defer func() { + _ = fl.Close() + _ = os.RemoveAll(dir) + }() + + var template *model.Record + require.NoError(t, jsonw.Decode(strings.NewReader(testRecordTemplate), &template)) + + ctx := context.Background() + + err := fl.ImportBlock(ctx, 2, nil) + require.NoError(t, err) + + topBlock, err := fl.GetTopBlock(ctx) + require.NoError(t, err) + + require.Equal(t, uint64(2), topBlock.Number) + + rec, err := fl.GetBlockRecords(ctx, 2) + require.NoError(t, err) + assert.Equal(t, 0, len(rec)) } diff --git a/ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc b/ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc index 0ec5907..6a0778b 100644 --- a/ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc +++ b/ledger/onflow/templates/scripts/metalocker_get_data_asset_counter.cdc @@ -1,7 +1,7 @@ {{ define "metalocker_get_data_asset_counter" }} import MetaLocker from {{.MetaLocker}} -access(all) fun main(id: String) : UInt64 { +access(all) fun main(id: String) : UInt64? { return MetaLocker.getDataAssetCounter(id: id) } {{ end }} diff --git a/ledger/onflow/templates/transactions/metalocker_import_block.cdc b/ledger/onflow/templates/transactions/metalocker_import_block.cdc new file mode 100644 index 0000000..cb607d0 --- /dev/null +++ b/ledger/onflow/templates/transactions/metalocker_import_block.cdc @@ -0,0 +1,9 @@ +{{ define "metalocker_import_block" }} +import MetaLocker from {{.MetaLocker}} + +transaction(number: UInt64, records: [MetaLocker.Record]) { + execute { + MetaLocker.importBlock(number: number, records: records) + } +} +{{ end }} diff --git a/model/access_token.go b/model/access_token.go index f437fd6..46025ec 100644 --- a/model/access_token.go +++ b/model/access_token.go @@ -61,6 +61,10 @@ func VerifyAccessToken(ctx context.Context, at, dataAssetID string, now, maxDist log.Info().Str("daid", dataAssetID). Msg("Data asset isn't attached to any leases yet. Allow access.") return true + } else { + log.Error().Str("daid", dataAssetID).Int("state", int(state)). + Msg("Unexpected data asset state. Access denied.") + return false } } s := strings.Split(at, ".") diff --git a/model/ledger.go b/model/ledger.go index 95ec2e8..4cddafc 100644 --- a/model/ledger.go +++ b/model/ledger.go @@ -64,6 +64,10 @@ type ( // SubmitRecord adds a ledger records into the queue to be // included into the next block. SubmitRecord(ctx context.Context, r *Record) error + // ImportBlock attempts importing list of record as a whole block with the given number. + // This operation may not be supported by some ledgers. It's used + // for transferring chains of blocks between ledgers while preserving its structure. + ImportBlock(ctx context.Context, blockNumber uint64, records []*Record) error // GetRecord returns a ledger record by its ID. Returns ErrRecordNotFound error // if record was not found. GetRecord(ctx context.Context, rid string) (*Record, error) diff --git a/remote/caller/blocks.go b/remote/caller/blocks.go index c9394be..0b8b74e 100644 --- a/remote/caller/blocks.go +++ b/remote/caller/blocks.go @@ -18,12 +18,17 @@ import ( "bytes" "context" "encoding/csv" + "errors" "fmt" "net/http" "github.com/piprate/metalocker/model" ) +func (c *MetaLockerHTTPCaller) ImportBlock(ctx context.Context, blockNumber uint64, records []*model.Record) error { + return errors.New("remote operation not supported: ImportBlock") +} + func (c *MetaLockerHTTPCaller) GetGenesisBlock(ctx context.Context) (*model.Block, error) { var b model.Block err := c.client.LoadContents(ctx, http.MethodGet, "/v1/ledger/genesis", nil, &b) diff --git a/sdk/testbase/environment.go b/sdk/testbase/environment.go index 8cfe636..12f9967 100644 --- a/sdk/testbase/environment.go +++ b/sdk/testbase/environment.go @@ -228,13 +228,11 @@ func SetUpTestEnvironment(t *testing.T) *TestMetaLockerEnvironment { keyIndexes[i] = uint32(i + 1) } - ledger, err := onflow.NewLedger(context.Background(), flowConnector, "emulator", nodeAcct, keyIndexes, dbFilepath, env.NS) + ledger, err := onflow.NewLedger(env.Ctx, flowConnector, "emulator", nodeAcct, keyIndexes, dbFilepath, env.NS) require.NoError(t, err) require.NoError(t, ledger.StartLedgerEvents(env.Ctx, true, time.Second)) - //require.NoError(t, ledger.Sync(env.Ctx)) - ledgerAPI = ledger case "local": dbFilepath := filepath.Join(dir, "ledger.bolt") From 3b210c46b645c4c3018c925a1969b280023dcdf8 Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 23 Mar 2025 16:15:29 +0000 Subject: [PATCH 4/9] Bug Fix: dataset retrieval fails with Postgres storage backend without local index --- ledger/onflow/common_test.go | 12 ++++++++++++ ledger/onflow/contract_test.go | 2 +- remote/caller/identities.go | 10 +++++++--- remote/caller/lockers.go | 5 +++-- remote/caller/properties.go | 5 +++-- storage/memory/inmemory.go | 6 +++--- storage/rdb/backend.go | 6 +++--- wallet/local.go | 12 ++++++------ wallet/restricted.go | 6 +++--- wallet/storage.go | 6 +++--- wallet/wallet.go | 6 +++--- 11 files changed, 47 insertions(+), 29 deletions(-) diff --git a/ledger/onflow/common_test.go b/ledger/onflow/common_test.go index cd2a0af..ef6dd1a 100644 --- a/ledger/onflow/common_test.go +++ b/ledger/onflow/common_test.go @@ -126,6 +126,18 @@ func AssertDataAssetCounter(t *testing.T, se *splash.TemplateEngine, id string, } } +func AssertNoDataAssetCounter(t *testing.T, se *splash.TemplateEngine, id string) { + t.Helper() + + val, err := se.NewScript("metalocker_get_data_asset_counter"). + Argument(cadence.String(id)). + RunReturns(context.Background()) + require.NoError(t, err) + + opt, _ := val.(cadence.Optional) + assert.Nil(t, opt.Value) +} + func GetAssetHead(t *testing.T, se *splash.TemplateEngine, headID string) *model.Record { t.Helper() diff --git a/ledger/onflow/contract_test.go b/ledger/onflow/contract_test.go index ed8b511..e692adf 100644 --- a/ledger/onflow/contract_test.go +++ b/ledger/onflow/contract_test.go @@ -77,7 +77,7 @@ func TestMetaLocker_submitRecord_SequentialRecords(t *testing.T) { AssertDataAssetCounter(t, se, rec.DataAssets[0], 1) AssertDataAssetCounter(t, se, rec.OperationAddress, 1) - AssertDataAssetCounter(t, se, "non-existent-asset-id", 0) + AssertNoDataAssetCounter(t, se, "non-existent-asset-id") rec2 := rec.Copy() rec2.ID = "second-record" diff --git a/remote/caller/identities.go b/remote/caller/identities.go index 12e4a98..24d0332 100644 --- a/remote/caller/identities.go +++ b/remote/caller/identities.go @@ -20,6 +20,7 @@ import ( "fmt" "net/http" + "github.com/piprate/metalocker/model" "github.com/piprate/metalocker/model/account" "github.com/piprate/metalocker/sdk/httpsecure" "github.com/piprate/metalocker/storage" @@ -33,8 +34,8 @@ func (c *MetaLockerHTTPCaller) GetIdentity(ctx context.Context, hash string) (*a return c.getDataEnvelope(ctx, "identity", hash, storage.ErrIdentityNotFound) } -func (c *MetaLockerHTTPCaller) ListIdentities(ctx context.Context) ([]*account.DataEnvelope, error) { - return c.listDataEnvelopes(ctx, "identity") +func (c *MetaLockerHTTPCaller) ListIdentities(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { + return c.listDataEnvelopes(ctx, "identity", lvl) } func (c *MetaLockerHTTPCaller) storeDataEnvelope(ctx context.Context, entityType string, env *account.DataEnvelope) error { @@ -79,13 +80,16 @@ func (c *MetaLockerHTTPCaller) getDataEnvelope(ctx context.Context, entityType, } } -func (c *MetaLockerHTTPCaller) listDataEnvelopes(ctx context.Context, entityType string) ([]*account.DataEnvelope, error) { +func (c *MetaLockerHTTPCaller) listDataEnvelopes(ctx context.Context, entityType string, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { if !c.client.IsAuthenticated() { return nil, errors.New("you need to log in before performing any operations") } var envList []*account.DataEnvelope url := "/v1/account/" + c.currentAccountID + "/" + entityType + if lvl != model.AccessLevelNone { + url += fmt.Sprintf("?level=%d", lvl) + } err := c.client.LoadContents(ctx, http.MethodGet, url, nil, &envList) if err != nil { return nil, err diff --git a/remote/caller/lockers.go b/remote/caller/lockers.go index 27bdb55..a77d773 100644 --- a/remote/caller/lockers.go +++ b/remote/caller/lockers.go @@ -17,6 +17,7 @@ package caller import ( "context" + "github.com/piprate/metalocker/model" "github.com/piprate/metalocker/model/account" "github.com/piprate/metalocker/storage" ) @@ -29,6 +30,6 @@ func (c *MetaLockerHTTPCaller) GetLocker(ctx context.Context, hash string) (*acc return c.getDataEnvelope(ctx, "locker", hash, storage.ErrLockerNotFound) } -func (c *MetaLockerHTTPCaller) ListLockers(ctx context.Context) ([]*account.DataEnvelope, error) { - return c.listDataEnvelopes(ctx, "locker") +func (c *MetaLockerHTTPCaller) ListLockers(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { + return c.listDataEnvelopes(ctx, "locker", lvl) } diff --git a/remote/caller/properties.go b/remote/caller/properties.go index e74a69a..9c4ab14 100644 --- a/remote/caller/properties.go +++ b/remote/caller/properties.go @@ -17,6 +17,7 @@ package caller import ( "context" + "github.com/piprate/metalocker/model" "github.com/piprate/metalocker/model/account" "github.com/piprate/metalocker/storage" ) @@ -29,8 +30,8 @@ func (c *MetaLockerHTTPCaller) GetProperty(ctx context.Context, hash string) (*a return c.getDataEnvelope(ctx, "property", hash, storage.ErrPropertyNotFound) } -func (c *MetaLockerHTTPCaller) ListProperties(ctx context.Context) ([]*account.DataEnvelope, error) { - return c.listDataEnvelopes(ctx, "property") +func (c *MetaLockerHTTPCaller) ListProperties(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { + return c.listDataEnvelopes(ctx, "property", lvl) } func (c *MetaLockerHTTPCaller) DeleteProperty(ctx context.Context, hash string) error { diff --git a/storage/memory/inmemory.go b/storage/memory/inmemory.go index c8b84ff..8c0b5e4 100644 --- a/storage/memory/inmemory.go +++ b/storage/memory/inmemory.go @@ -180,7 +180,7 @@ func (be *InMemoryBackend) ListIdentities(ctx context.Context, accountID string, } res := make([]*account.DataEnvelope, 0, len(acctMap)) for _, idy := range acctMap { - if lvl == 0 || idy.AccessLevel == lvl { + if lvl == 0 || idy.AccessLevel <= lvl { res = append(res, idy) } } @@ -215,7 +215,7 @@ func (be *InMemoryBackend) ListLockers(ctx context.Context, accountID string, lv } res := make([]*account.DataEnvelope, 0, len(acctMap)) for _, locker := range acctMap { - if lvl == 0 || locker.AccessLevel == lvl { + if lvl == 0 || locker.AccessLevel <= lvl { res = append(res, locker) } } @@ -250,7 +250,7 @@ func (be *InMemoryBackend) ListProperties(ctx context.Context, accountID string, } res := make([]*account.DataEnvelope, 0, len(acctMap)) for _, prop := range acctMap { - if lvl == 0 || prop.AccessLevel == lvl { + if lvl == 0 || prop.AccessLevel <= lvl { res = append(res, prop) } } diff --git a/storage/rdb/backend.go b/storage/rdb/backend.go index 58dcccb..247e580 100644 --- a/storage/rdb/backend.go +++ b/storage/rdb/backend.go @@ -359,7 +359,7 @@ func (rbe *RelationalBackend) ListIdentities(ctx context.Context, accountID stri rows, err := rbe.client.Identity.Query(). Where( identity.HasAccountWith(entAccount.Did(accountID)), - identity.Level(int32(lvl)), + identity.LevelLTE(int32(lvl)), ). All(ctx) if err != nil { @@ -431,7 +431,7 @@ func (rbe *RelationalBackend) ListLockers(ctx context.Context, accountID string, rows, err := rbe.client.Locker.Query(). Where( locker.HasAccountWith(entAccount.Did(accountID)), - locker.Level(int32(lvl)), + locker.LevelLTE(int32(lvl)), ). All(ctx) if err != nil { @@ -503,7 +503,7 @@ func (rbe *RelationalBackend) ListProperties(ctx context.Context, accountID stri rows, err := rbe.client.Property.Query(). Where( property.HasAccountWith(entAccount.Did(accountID)), - property.Level(int32(lvl)), + property.LevelLTE(int32(lvl)), ). All(ctx) if err != nil { diff --git a/wallet/local.go b/wallet/local.go index 422ecf8..fd091c0 100644 --- a/wallet/local.go +++ b/wallet/local.go @@ -153,8 +153,8 @@ func (lnc *LocalNodeClient) GetIdentity(ctx context.Context, hash string) (*acco return lnc.identityBackend.GetIdentity(ctx, lnc.accountID, hash) } -func (lnc *LocalNodeClient) ListIdentities(ctx context.Context) ([]*account.DataEnvelope, error) { - return lnc.identityBackend.ListIdentities(ctx, lnc.accountID, 0) +func (lnc *LocalNodeClient) ListIdentities(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { + return lnc.identityBackend.ListIdentities(ctx, lnc.accountID, lvl) } func (lnc *LocalNodeClient) StoreLocker(ctx context.Context, l *account.DataEnvelope) error { @@ -165,8 +165,8 @@ func (lnc *LocalNodeClient) GetLocker(ctx context.Context, hash string) (*accoun return lnc.identityBackend.GetLocker(ctx, lnc.accountID, hash) } -func (lnc *LocalNodeClient) ListLockers(ctx context.Context) ([]*account.DataEnvelope, error) { - return lnc.identityBackend.ListLockers(ctx, lnc.accountID, 0) +func (lnc *LocalNodeClient) ListLockers(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { + return lnc.identityBackend.ListLockers(ctx, lnc.accountID, lvl) } func (lnc *LocalNodeClient) StoreProperty(ctx context.Context, prop *account.DataEnvelope) error { @@ -177,8 +177,8 @@ func (lnc *LocalNodeClient) GetProperty(ctx context.Context, hash string) (*acco return lnc.identityBackend.GetProperty(ctx, lnc.accountID, hash) } -func (lnc *LocalNodeClient) ListProperties(ctx context.Context) ([]*account.DataEnvelope, error) { - return lnc.identityBackend.ListProperties(ctx, lnc.accountID, 0) +func (lnc *LocalNodeClient) ListProperties(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { + return lnc.identityBackend.ListProperties(ctx, lnc.accountID, lvl) } func (lnc *LocalNodeClient) DeleteProperty(ctx context.Context, hash string) error { diff --git a/wallet/restricted.go b/wallet/restricted.go index cc5d3f9..81e5aa4 100644 --- a/wallet/restricted.go +++ b/wallet/restricted.go @@ -114,7 +114,7 @@ func (r *RestrictedNodeClient) GetIdentity(ctx context.Context, hash string) (*a panic("operation not implemented") } -func (r *RestrictedNodeClient) ListIdentities(ctx context.Context) ([]*account.DataEnvelope, error) { +func (r *RestrictedNodeClient) ListIdentities(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { //idyList, err := r.nodeClient.ListIdentities() //if err != nil { // return nil, err @@ -132,7 +132,7 @@ func (r *RestrictedNodeClient) GetLocker(ctx context.Context, hash string) (*acc panic("operation not implemented") } -func (r *RestrictedNodeClient) ListLockers(ctx context.Context) ([]*account.DataEnvelope, error) { +func (r *RestrictedNodeClient) ListLockers(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { panic("operation not implemented") } @@ -144,7 +144,7 @@ func (r *RestrictedNodeClient) GetProperty(ctx context.Context, hash string) (*a panic("operation not implemented") } -func (r *RestrictedNodeClient) ListProperties(ctx context.Context) ([]*account.DataEnvelope, error) { +func (r *RestrictedNodeClient) ListProperties(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) { panic("operation not implemented") } diff --git a/wallet/storage.go b/wallet/storage.go index 66ab60b..74338f0 100644 --- a/wallet/storage.go +++ b/wallet/storage.go @@ -52,15 +52,15 @@ type ( StoreIdentity(ctx context.Context, idy *account.DataEnvelope) error GetIdentity(ctx context.Context, hash string) (*account.DataEnvelope, error) - ListIdentities(ctx context.Context) ([]*account.DataEnvelope, error) + ListIdentities(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) StoreLocker(ctx context.Context, l *account.DataEnvelope) error GetLocker(ctx context.Context, hash string) (*account.DataEnvelope, error) - ListLockers(ctx context.Context) ([]*account.DataEnvelope, error) + ListLockers(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) StoreProperty(ctx context.Context, prop *account.DataEnvelope) error GetProperty(ctx context.Context, hash string) (*account.DataEnvelope, error) - ListProperties(ctx context.Context) ([]*account.DataEnvelope, error) + ListProperties(ctx context.Context, lvl model.AccessLevel) ([]*account.DataEnvelope, error) DeleteProperty(ctx context.Context, hash string) error } ) diff --git a/wallet/wallet.go b/wallet/wallet.go index 7972eac..29f06fc 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -790,7 +790,7 @@ func (dw *LocalDataWallet) GetIdentities(ctx context.Context) (map[string]Identi return dw.identities, nil } - idyEnvList, err := dw.nodeClient.ListIdentities(ctx) + idyEnvList, err := dw.nodeClient.ListIdentities(ctx, dw.lockLevel) if err != nil { return nil, err } @@ -866,7 +866,7 @@ func (dw *LocalDataWallet) GetLockers(ctx context.Context) ([]*model.Locker, err return nil, ErrWalletLocked } - lockerEnvList, err := dw.nodeClient.ListLockers(ctx) + lockerEnvList, err := dw.nodeClient.ListLockers(ctx, dw.lockLevel) if err != nil { return nil, err } @@ -970,7 +970,7 @@ func (dw *LocalDataWallet) GetProperties(ctx context.Context) (map[string]string return nil, ErrWalletLocked } - envList, err := dw.nodeClient.ListProperties(ctx) + envList, err := dw.nodeClient.ListProperties(ctx, dw.lockLevel) if err != nil { return nil, err } From 90d559da51d6bfe030058b34b184920ecaf0a225 Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 23 Mar 2025 16:34:35 +0000 Subject: [PATCH 5/9] Fix linter errors --- cmd/metalo/operations/import_ledger.go | 8 ++++---- ledger/local/ledger.go | 2 +- ledger/onflow/common_test.go | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd/metalo/operations/import_ledger.go b/cmd/metalo/operations/import_ledger.go index 24c719e..9513b7e 100644 --- a/cmd/metalo/operations/import_ledger.go +++ b/cmd/metalo/operations/import_ledger.go @@ -36,7 +36,7 @@ import ( "github.com/urfave/cli/v2" ) -func ImportLedger(ctx context.Context, importDirPath, configFilePath string, importOperations, preserveBlocks, waitForConfirmation bool) error { +func ImportLedger(ctx context.Context, importDirPath, configFilePath string, importOperations, preserveBlocks, waitForConfirmation bool) error { //nolint:gocyclo // read configuration cfg, err := readConfigFile(configFilePath) if err != nil { @@ -48,7 +48,7 @@ func ImportLedger(ctx context.Context, importDirPath, configFilePath string, imp return err } - destLedger, err := initLedgerInstance(cfg, resolver) + destLedger, err := initLedgerInstance(ctx, cfg, resolver) if err != nil { return err } @@ -201,7 +201,7 @@ func ImportLedger(ctx context.Context, importDirPath, configFilePath string, imp return nil } -func initLedgerInstance(cfg *koanf.Koanf, resolver cmdbase.ParameterResolver) (model.Ledger, error) { +func initLedgerInstance(ctx context.Context, cfg *koanf.Koanf, resolver cmdbase.ParameterResolver) (model.Ledger, error) { if cfg.Exists("ledger") { var ledgerCfg *ledger.Config @@ -211,7 +211,7 @@ func initLedgerInstance(cfg *koanf.Koanf, resolver cmdbase.ParameterResolver) (m return nil, cli.Exit(err, 1) } - identityBackend, err := ledger.CreateLedgerConnector(context.Background(), ledgerCfg, nil, resolver) + identityBackend, err := ledger.CreateLedgerConnector(ctx, ledgerCfg, nil, resolver) if err != nil { log.Err(err).Msg("Failed to create ledger instance") return nil, cli.Exit(err, 1) diff --git a/ledger/local/ledger.go b/ledger/local/ledger.go index bb32d56..19c2af0 100644 --- a/ledger/local/ledger.go +++ b/ledger/local/ledger.go @@ -908,7 +908,7 @@ func generateNonce(seed string, size int) ([]byte, error) { return nonce, nil } -func generateNewBlock(ctx context.Context, bl *BoltLedger, seed string) error { +func generateNewBlock(ctx context.Context, bl *BoltLedger, seed string) error { //nolint:unparam records := make([]*model.Record, 0) for _, id := range bl.pendingRecords { diff --git a/ledger/onflow/common_test.go b/ledger/onflow/common_test.go index ef6dd1a..80c7698 100644 --- a/ledger/onflow/common_test.go +++ b/ledger/onflow/common_test.go @@ -2,7 +2,6 @@ package onflow_test import ( "context" - "fmt" "strings" "testing" @@ -120,7 +119,7 @@ func AssertDataAssetCounter(t *testing.T, se *splash.TemplateEngine, id string, opt, _ := val.(cadence.Optional) if opt.Value == nil { - assert.Fail(t, fmt.Sprintf("data asset counter is nil")) + assert.Fail(t, "data asset counter is nil") } else { assert.Equal(t, counter, uint64(opt.Value.(cadence.UInt64))) } From c8ec4a88b878d04cc4e15b2c89a8ea19ffd264bc Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 23 Mar 2025 16:35:16 +0000 Subject: [PATCH 6/9] Update dependencies to address dependabot warnings --- go.mod | 16 ++++++++-------- go.sum | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index c9057a4..7cd7519 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/piprate/metalocker -go 1.22.0 +go 1.23.0 -toolchain go1.23.3 +toolchain go1.23.4 require ( entgo.io/ent v0.11.6 @@ -20,7 +20,7 @@ require ( github.com/gin-contrib/cors v1.7.2 github.com/gin-gonic/gin v1.10.0 github.com/gobuffalo/packr/v2 v2.8.3 - github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang-migrate/migrate/v4 v4.18.1 github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.3 @@ -44,8 +44,8 @@ require ( github.com/urfave/cli/v2 v2.27.5 github.com/xlab/treeprint v1.2.0 go.etcd.io/bbolt v1.3.11 - golang.org/x/crypto v0.29.0 - golang.org/x/term v0.26.0 + golang.org/x/crypto v0.36.0 + golang.org/x/term v0.30.0 ) require ( @@ -297,9 +297,9 @@ require ( golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.31.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gonum.org/v1/gonum v0.14.0 // indirect diff --git a/go.sum b/go.sum index 78c7d59..bb6be61 100644 --- a/go.sum +++ b/go.sum @@ -420,8 +420,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y= github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -1287,8 +1287,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1408,8 +1408,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1497,12 +1497,12 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1514,8 +1514,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 108a22334c19874654925afa3c0f8b18958b1ba8 Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 23 Mar 2025 18:17:34 +0000 Subject: [PATCH 7/9] Update Postgres storage logic + tests --- storage/rdb/backend.go | 6 +++--- storage/rdb/backend_test.go | 24 ++++++++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/storage/rdb/backend.go b/storage/rdb/backend.go index 247e580..f62b9f5 100644 --- a/storage/rdb/backend.go +++ b/storage/rdb/backend.go @@ -371,7 +371,7 @@ func (rbe *RelationalBackend) ListIdentities(ctx context.Context, accountID stri for i, row := range rows { result[i] = &account.DataEnvelope{ Hash: row.Hash, - AccessLevel: lvl, + AccessLevel: model.AccessLevel(row.Level), EncryptedID: row.EncryptedID, EncryptedBody: row.EncryptedBody, } @@ -443,7 +443,7 @@ func (rbe *RelationalBackend) ListLockers(ctx context.Context, accountID string, for i, row := range rows { result[i] = &account.DataEnvelope{ Hash: row.Hash, - AccessLevel: lvl, + AccessLevel: model.AccessLevel(row.Level), EncryptedID: row.EncryptedID, EncryptedBody: row.EncryptedBody, } @@ -515,7 +515,7 @@ func (rbe *RelationalBackend) ListProperties(ctx context.Context, accountID stri for i, row := range rows { result[i] = &account.DataEnvelope{ Hash: row.Hash, - AccessLevel: lvl, + AccessLevel: model.AccessLevel(row.Level), EncryptedID: row.EncryptedID, EncryptedBody: row.EncryptedBody, } diff --git a/storage/rdb/backend_test.go b/storage/rdb/backend_test.go index 6b41bc7..5ee57ca 100644 --- a/storage/rdb/backend_test.go +++ b/storage/rdb/backend_test.go @@ -375,13 +375,21 @@ func Test_RelationalBackend_ListIdentities(t *testing.T) { // happy paths - list, err := be.ListIdentities(ctx, resp.Account.ID, model.AccessLevelManaged) + list, err := be.ListIdentities(ctx, resp.Account.ID, model.AccessLevelNone) + require.NoError(t, err) + assert.Equal(t, 0, len(list)) + + list, err = be.ListIdentities(ctx, resp.Account.ID, model.AccessLevelRestricted) + require.NoError(t, err) + assert.Equal(t, 0, len(list)) + + list, err = be.ListIdentities(ctx, resp.Account.ID, model.AccessLevelManaged) require.NoError(t, err) assert.Equal(t, 1, len(list)) list, err = be.ListIdentities(ctx, resp.Account.ID, model.AccessLevelHosted) require.NoError(t, err) - assert.Equal(t, 0, len(list)) + assert.Equal(t, 1, len(list)) _ = client.Close() _, err = be.ListIdentities(ctx, resp.Account.ID, model.AccessLevelHosted) @@ -637,13 +645,21 @@ func Test_RelationalBackend_ListLockers(t *testing.T) { // happy paths - list, err := be.ListLockers(ctx, resp.Account.ID, model.AccessLevelManaged) + list, err := be.ListLockers(ctx, resp.Account.ID, model.AccessLevelNone) + require.NoError(t, err) + assert.Equal(t, 0, len(list)) + + list, err = be.ListLockers(ctx, resp.Account.ID, model.AccessLevelRestricted) + require.NoError(t, err) + assert.Equal(t, 0, len(list)) + + list, err = be.ListLockers(ctx, resp.Account.ID, model.AccessLevelManaged) require.NoError(t, err) assert.Equal(t, 1, len(list)) list, err = be.ListLockers(ctx, resp.Account.ID, model.AccessLevelHosted) require.NoError(t, err) - assert.Equal(t, 1, len(list)) + assert.Equal(t, 2, len(list)) _ = client.Close() _, err = be.ListLockers(ctx, resp.Account.ID, model.AccessLevelHosted) From 2cc199652bf6b01b26e19123cf181219f009df42 Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 23 Mar 2025 18:18:17 +0000 Subject: [PATCH 8/9] Upgrade CI to Go 1.23 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34b026e..b343688 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.22 + go-version: 1.23 - name: Update packages list run: sudo apt-get update @@ -63,7 +63,7 @@ jobs: uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.22 + go-version: 1.23 - uses: actions/cache@v3 with: path: | @@ -75,7 +75,7 @@ jobs: - uses: zencargo/github-action-go-mod-tidy@v1 with: path: . - go-version: 1.22 + go-version: 1.23 test: runs-on: ubuntu-latest @@ -89,7 +89,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.22 + go-version: 1.23 - name: Set up Cache uses: actions/cache@v3 with: From 8d7228633d422f980634cf888554709a13225986 Mon Sep 17 00:00:00 2001 From: sequel21 Date: Sun, 23 Mar 2025 19:40:54 +0000 Subject: [PATCH 9/9] Clean up unused code --- ledger/onflow/ledger.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/ledger/onflow/ledger.go b/ledger/onflow/ledger.go index 6c3996b..83c6d68 100644 --- a/ledger/onflow/ledger.go +++ b/ledger/onflow/ledger.go @@ -696,24 +696,6 @@ func (l *Ledger) GetRecord(ctx context.Context, rid string) (*model.Record, erro return nil, model.ErrRecordNotFound } } - - //val, err := l.scriptsEngine.NewScript("metalocker_get_record"). - // Argument(cadence.String(rid)). - // RunReturns(ctx) - //if err != nil { - // return nil, err - //} - // - //rec, err := RecordFromCadence(val) - //if err != nil { - // return nil, err - //} - // - //if rec != nil { - // return rec, nil - //} else { - // return nil, model.ErrRecordNotFound - //} } func (l *Ledger) GetRecordState(ctx context.Context, rid string) (*model.RecordState, error) {