From b9c23ffbb2eed0b3cd9f602dad38928dc078596c Mon Sep 17 00:00:00 2001 From: ravi0131 Date: Tue, 14 May 2024 18:08:59 +0200 Subject: [PATCH 1/4] payment-channel for ckb --- payment-channel-ckb/.gitignore | 27 + payment-channel-ckb/.gitmodules | 3 + payment-channel-ckb/.idea/.gitignore | 8 + payment-channel-ckb/.idea/modules.xml | 8 + .../.idea/payment-channel-ckb.iml | 9 + payment-channel-ckb/.idea/vcs.xml | 6 + payment-channel-ckb/.vscode/launch.json | 17 + payment-channel-ckb/client/balances.go | 89 + payment-channel-ckb/client/channel.go | 91 + payment-channel-ckb/client/client.go | 252 + payment-channel-ckb/client/handle.go | 92 + payment-channel-ckb/client/util.go | 22 + payment-channel-ckb/deployment/deployment.go | 159 + payment-channel-ckb/deployment/keys.go | 33 + .../deployment/system_scripts.go | 58 + .../deployment/system_scripts_test.go | 21 + payment-channel-ckb/devnet/Makefile | 2 + .../devnet/contracts/.assets/go-perun.png | Bin 0 -> 150688 bytes .../contracts/.github/workflows/rust.yml | 38 + .../devnet/contracts/.gitignore | 14 + .../devnet/contracts/Cargo.lock | 842 +++ .../devnet/contracts/Cargo.toml | 15 + .../devnet/contracts/README.md | 45 + .../devnet/contracts/build/.gitkeep | 0 .../devnet/contracts/capsule.toml | 27 + .../devnet/contracts/contracts/.gitkeep | 0 .../perun-channel-lockscript/Cargo.toml | 10 + .../perun-channel-lockscript/src/entry.rs | 72 + .../perun-channel-lockscript/src/main.rs | 32 + .../perun-channel-typescript/Cargo.toml | 10 + .../perun-channel-typescript/src/entry.rs | 904 +++ .../perun-channel-typescript/src/main.rs | 32 + .../contracts/perun-common/Cargo.toml | 27 + .../contracts/perun-common/blockchain.mol | 108 + .../contracts/perun-common/offchain_types.mol | 8 + .../contracts/perun-common/src/error.rs | 115 + .../contracts/perun-common/src/helpers.rs | 448 ++ .../contracts/perun-common/src/lib.rs | 7 + .../contracts/perun-common/src/perun_types.rs | 6561 +++++++++++++++++ .../contracts/perun-common/src/sig.rs | 11 + .../contracts/perun-common/types.mol | 140 + .../perun-funds-lockscript/Cargo.toml | 10 + .../perun-funds-lockscript/src/entry.rs | 47 + .../perun-funds-lockscript/src/main.rs | 32 + .../contracts/contracts/sample-udt/Cargo.toml | 10 + .../contracts/sample-udt/src/entry.rs | 101 + .../contracts/sample-udt/src/main.rs | 32 + .../contracts/deployment/dev/deployment.toml | 41 + .../deployment/release/deployment.toml | 37 + .../devnet/contracts/migrations/.gitkeep | 0 .../devnet/contracts/tests/Cargo.lock | 1593 ++++ .../devnet/contracts/tests/Cargo.toml | 18 + .../devnet/contracts/tests/src/lib.rs | 63 + .../contracts/tests/src/perun/account.rs | 41 + .../contracts/tests/src/perun/action.rs | 22 + .../contracts/tests/src/perun/channel.rs | 374 + .../devnet/contracts/tests/src/perun/error.rs | 65 + .../contracts/tests/src/perun/harness.rs | 285 + .../devnet/contracts/tests/src/perun/mod.rs | 22 + .../contracts/tests/src/perun/mutators.rs | 69 + .../contracts/tests/src/perun/random.rs | 13 + .../devnet/contracts/tests/src/perun/state.rs | 17 + .../contracts/tests/src/perun/test/cell.rs | 74 + .../tests/src/perun/test/channel_id.rs | 38 + .../contracts/tests/src/perun/test/client.rs | 259 + .../tests/src/perun/test/funding_agreement.rs | 282 + .../contracts/tests/src/perun/test/keys.rs | 11 + .../contracts/tests/src/perun/test/mod.rs | 15 + .../tests/src/perun/test/transaction/abort.rs | 85 + .../tests/src/perun/test/transaction/close.rs | 96 + .../src/perun/test/transaction/common.rs | 41 + .../src/perun/test/transaction/dispute.rs | 94 + .../src/perun/test/transaction/force_close.rs | 94 + .../tests/src/perun/test/transaction/fund.rs | 123 + .../tests/src/perun/test/transaction/mod.rs | 19 + .../tests/src/perun/test/transaction/open.rs | 129 + .../devnet/contracts/tests/src/tests.rs | 486 ++ .../devnet/deploy_contracts.sh | 51 + .../devnet/devnet-session.yaml | 26 + .../devnet/fund_accounts.expect | 7 + payment-channel-ckb/devnet/fund_accounts.sh | 33 + payment-channel-ckb/devnet/print_accounts.sh | 13 + payment-channel-ckb/devnet/setup-devnet.sh | 96 + .../devnet/sudt-celldep-template.json | 17 + payment-channel-ckb/devnet/sudt_helper.sh | 66 + payment-channel-ckb/go.mod | 45 + payment-channel-ckb/go.sum | 1322 ++++ payment-channel-ckb/main.go | 258 + 88 files changed, 17035 insertions(+) create mode 100644 payment-channel-ckb/.gitignore create mode 100644 payment-channel-ckb/.gitmodules create mode 100644 payment-channel-ckb/.idea/.gitignore create mode 100644 payment-channel-ckb/.idea/modules.xml create mode 100644 payment-channel-ckb/.idea/payment-channel-ckb.iml create mode 100644 payment-channel-ckb/.idea/vcs.xml create mode 100644 payment-channel-ckb/.vscode/launch.json create mode 100644 payment-channel-ckb/client/balances.go create mode 100644 payment-channel-ckb/client/channel.go create mode 100644 payment-channel-ckb/client/client.go create mode 100644 payment-channel-ckb/client/handle.go create mode 100644 payment-channel-ckb/client/util.go create mode 100644 payment-channel-ckb/deployment/deployment.go create mode 100644 payment-channel-ckb/deployment/keys.go create mode 100644 payment-channel-ckb/deployment/system_scripts.go create mode 100644 payment-channel-ckb/deployment/system_scripts_test.go create mode 100644 payment-channel-ckb/devnet/Makefile create mode 100644 payment-channel-ckb/devnet/contracts/.assets/go-perun.png create mode 100644 payment-channel-ckb/devnet/contracts/.github/workflows/rust.yml create mode 100644 payment-channel-ckb/devnet/contracts/.gitignore create mode 100644 payment-channel-ckb/devnet/contracts/Cargo.lock create mode 100644 payment-channel-ckb/devnet/contracts/Cargo.toml create mode 100644 payment-channel-ckb/devnet/contracts/README.md create mode 100644 payment-channel-ckb/devnet/contracts/build/.gitkeep create mode 100644 payment-channel-ckb/devnet/contracts/capsule.toml create mode 100644 payment-channel-ckb/devnet/contracts/contracts/.gitkeep create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/Cargo.toml create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/entry.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/main.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/Cargo.toml create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/entry.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/main.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/Cargo.toml create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/blockchain.mol create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/offchain_types.mol create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/src/error.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/src/helpers.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/src/lib.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/src/perun_types.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/src/sig.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-common/types.mol create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/Cargo.toml create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/entry.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/main.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/sample-udt/Cargo.toml create mode 100644 payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/entry.rs create mode 100644 payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/main.rs create mode 100644 payment-channel-ckb/devnet/contracts/deployment/dev/deployment.toml create mode 100644 payment-channel-ckb/devnet/contracts/deployment/release/deployment.toml create mode 100644 payment-channel-ckb/devnet/contracts/migrations/.gitkeep create mode 100644 payment-channel-ckb/devnet/contracts/tests/Cargo.lock create mode 100644 payment-channel-ckb/devnet/contracts/tests/Cargo.toml create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/lib.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/account.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/action.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/channel.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/error.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/harness.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/mod.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/mutators.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/random.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/state.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/cell.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/channel_id.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/client.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/funding_agreement.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/keys.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/mod.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/abort.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/close.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/common.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/dispute.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/force_close.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/fund.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/mod.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/open.rs create mode 100644 payment-channel-ckb/devnet/contracts/tests/src/tests.rs create mode 100755 payment-channel-ckb/devnet/deploy_contracts.sh create mode 100644 payment-channel-ckb/devnet/devnet-session.yaml create mode 100644 payment-channel-ckb/devnet/fund_accounts.expect create mode 100755 payment-channel-ckb/devnet/fund_accounts.sh create mode 100755 payment-channel-ckb/devnet/print_accounts.sh create mode 100755 payment-channel-ckb/devnet/setup-devnet.sh create mode 100644 payment-channel-ckb/devnet/sudt-celldep-template.json create mode 100755 payment-channel-ckb/devnet/sudt_helper.sh create mode 100644 payment-channel-ckb/go.mod create mode 100644 payment-channel-ckb/go.sum create mode 100644 payment-channel-ckb/main.go diff --git a/payment-channel-ckb/.gitignore b/payment-channel-ckb/.gitignore new file mode 100644 index 0000000..146c59d --- /dev/null +++ b/payment-channel-ckb/.gitignore @@ -0,0 +1,27 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# log files +*.log + +perun-ckb-demo + +# Dependency directories (remove the comment below to include it) +# vendor/ +devnet/accounts +devnet/data +devnet/specs +devnet/ckb-miner.toml +devnet/ckb.toml +devnet/default.db-options +devnet/system_scripts diff --git a/payment-channel-ckb/.gitmodules b/payment-channel-ckb/.gitmodules new file mode 100644 index 0000000..5bc5ff8 --- /dev/null +++ b/payment-channel-ckb/.gitmodules @@ -0,0 +1,3 @@ +[submodule "devnet/contracts"] + path = devnet/contracts + url = git@github.com:perun-network/perun-ckb-contract diff --git a/payment-channel-ckb/.idea/.gitignore b/payment-channel-ckb/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/payment-channel-ckb/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/payment-channel-ckb/.idea/modules.xml b/payment-channel-ckb/.idea/modules.xml new file mode 100644 index 0000000..8b4f43e --- /dev/null +++ b/payment-channel-ckb/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/payment-channel-ckb/.idea/payment-channel-ckb.iml b/payment-channel-ckb/.idea/payment-channel-ckb.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/payment-channel-ckb/.idea/payment-channel-ckb.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/payment-channel-ckb/.idea/vcs.xml b/payment-channel-ckb/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/payment-channel-ckb/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/payment-channel-ckb/.vscode/launch.json b/payment-channel-ckb/.vscode/launch.json new file mode 100644 index 0000000..2a17be5 --- /dev/null +++ b/payment-channel-ckb/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}" + } + + ] +} \ No newline at end of file diff --git a/payment-channel-ckb/client/balances.go b/payment-channel-ckb/client/balances.go new file mode 100644 index 0000000..a322278 --- /dev/null +++ b/payment-channel-ckb/client/balances.go @@ -0,0 +1,89 @@ +package client + +import ( + "context" + "encoding/binary" + "fmt" + "log" + "math" + "math/big" + "strconv" + "time" + + "github.com/nervosnetwork/ckb-sdk-go/v2/indexer" + "github.com/nervosnetwork/ckb-sdk-go/v2/types" + "perun.network/perun-ckb-backend/wallet/address" +) + +type BalanceExtractor func(*indexer.LiveCell) *big.Int + +func ckbBalanceExtractor(cell *indexer.LiveCell) *big.Int { + return new(big.Int).SetUint64(cell.Output.Capacity) +} + +func sudtBalanceExtractor(cell *indexer.LiveCell) *big.Int { + if len(cell.OutputData) != 16 { + return big.NewInt(0) + } + return new(big.Int).SetUint64(binary.LittleEndian.Uint64(cell.OutputData)) +} + +func (p *PaymentClient) PollBalances() { + defer log.Println("PollBalances: stopped") + pollingInterval := time.Second + searchKey := &indexer.SearchKey{ + Script: address.AsParticipant(p.Account.Address()).PaymentScript, + ScriptType: types.ScriptTypeLock, + ScriptSearchMode: types.ScriptSearchModeExact, + Filter: nil, + WithData: true, + } + log.Println("PollBalances") + updateBalance := func() { + ctx, _ := context.WithTimeout(context.Background(), pollingInterval) + + cells, err := p.rpcClient.GetCells(ctx, searchKey, indexer.SearchOrderDesc, math.MaxUint32, "") + if err != nil { + log.Println("balance poll error: ", err) + return + } + ckbBalance := big.NewInt(0) + sudtBalance := big.NewInt(0) + for _, cell := range cells.Objects { + ckbBalance = new(big.Int).Add(ckbBalance, ckbBalanceExtractor(cell)) + sudtBalance = new(big.Int).Add(sudtBalance, sudtBalanceExtractor(cell)) + } + + p.balanceMutex.Lock() + if ckbBalance.Cmp(p.balance) != 0 || sudtBalance.Cmp(p.sudtBalance) != 0 { + // Update ckb balance. + p.balance = ckbBalance + //ckbBal := p.balance.Int64() + + // Update sudt balance. + p.sudtBalance = sudtBalance + + p.balanceMutex.Unlock() + //p.NotifyAllBalance(ckbBal) // TODO: Update demo tui to allow for big.Int balances + } else { + p.balanceMutex.Unlock() + } + } + updateBalance() + //return "CKbytes: " + p.balance.String() + ", SUDT: " + p.sudtBalance.String() + /* + // Poll the balance every 5 seconds. + for { + updateBalance() + time.Sleep(pollingInterval) + } + */ +} + +func FormatBalance(ckbBal, sudtBal *big.Int) string { + log.Printf("balances: ckb = %s || sudt = %s", ckbBal.String(), sudtBal.String()) + balCKByte, _ := ShannonToCKByte(ckbBal).Float64() + return fmt.Sprintf("[green]%s\t[yellow]%s[white]", + strconv.FormatFloat(balCKByte, 'f', 2, 64)+" CKByte", + fmt.Sprintf("%v", sudtBal.Int64())+" SUDT") +} diff --git a/payment-channel-ckb/client/channel.go b/payment-channel-ckb/client/channel.go new file mode 100644 index 0000000..05a5b61 --- /dev/null +++ b/payment-channel-ckb/client/channel.go @@ -0,0 +1,91 @@ +package client + +import ( + "context" + "fmt" + "math/big" + + "perun.network/go-perun/channel" + "perun.network/go-perun/client" +) + +type PaymentChannel struct { + ch *client.Channel + assets []channel.Asset +} + +// newPaymentChannel creates a new payment channel. +func newPaymentChannel(ch *client.Channel, assets []channel.Asset) *PaymentChannel { + return &PaymentChannel{ + ch: ch, + assets: assets, + } +} + +func (c PaymentChannel) State() *channel.State { + return c.ch.State().Clone() +} + +func (c PaymentChannel) SendPayment(amounts map[channel.Asset]float64) { + // Transfer the given amount from us to peer. + // Use UpdateBy to update the channel state. + err := c.ch.Update(context.TODO(), func(state *channel.State) { + actor := c.ch.Idx() + peer := 1 - actor + //fmt.Println("Send payment handler called") + for a, amount := range amounts { + fmt.Println(a) + fmt.Println(amount) + if amount < 0 { + continue + } + /* + switch a := a.(type) { + case *asset.Asset: + if a.IsCKBytes { + fmt.Println("inside condition isCKBytes") + shannonAmount := CKByteToShannon(big.NewFloat(amount)) + state.Allocation.TransferBalance(actor, peer, a, shannonAmount) + } else { + fmt.Println("inside if conditional !isCKBytes") + intAmount := new(big.Int).SetUint64(uint64(amount)) + state.Allocation.TransferBalance(actor, peer, a, intAmount) + } + } + */ + + shannonAmount := CKByteToShannon(big.NewFloat(amount)) + state.Allocation.TransferBalance(actor, peer, a, shannonAmount) + + } + + }) + if err != nil { + panic(err) + } + if err != nil { + panic(err) // We panic on error to keep the code simple. + } +} + +// Settle settles the payment channel and withdraws the funds. +func (c PaymentChannel) Settle() { + // Finalize the channel to enable fast settlement. + if !c.ch.State().IsFinal { + err := c.ch.Update(context.TODO(), func(state *channel.State) { + state.IsFinal = true + }) + if err != nil { + panic(err) + } + } + + // Settle concludes the channel and withdraws the funds. + err := c.ch.Settle(context.TODO()) + if err != nil { + panic(err) + } + + // Close frees up channel resources. + c.ch.Close() +} diff --git a/payment-channel-ckb/client/client.go b/payment-channel-ckb/client/client.go new file mode 100644 index 0000000..90d62d8 --- /dev/null +++ b/payment-channel-ckb/client/client.go @@ -0,0 +1,252 @@ +package client + +import ( + "context" + "fmt" + "log" + "math/big" + + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/nervosnetwork/ckb-sdk-go/v2/rpc" + "github.com/nervosnetwork/ckb-sdk-go/v2/types" + "perun.network/go-perun/channel" + gpchannel "perun.network/go-perun/channel" + "perun.network/go-perun/channel/persistence" + "perun.network/go-perun/client" + gpwallet "perun.network/go-perun/wallet" + "perun.network/go-perun/watcher/local" + "perun.network/go-perun/wire" + "perun.network/go-perun/wire/net/simple" + "perun.network/perun-ckb-backend/backend" + "perun.network/perun-ckb-backend/channel/adjudicator" + "perun.network/perun-ckb-backend/channel/asset" + "perun.network/perun-ckb-backend/channel/funder" + ckbclient "perun.network/perun-ckb-backend/client" + "perun.network/perun-ckb-backend/wallet" + "perun.network/perun-ckb-backend/wallet/address" + "polycry.pt/poly-go/sync" +) + +type PaymentClient struct { + balanceMutex sync.Mutex + Name string + balance *big.Int + sudtBalance *big.Int + Account *wallet.Account + wAddr wire.Address + Network types.Network + PerunClient *client.Client + + channels chan *PaymentChannel + rpcClient rpc.Client +} + +func NewPaymentClient( + name string, + network types.Network, + deployment backend.Deployment, + bus wire.Bus, + rpcUrl string, + account *wallet.Account, + key secp256k1.PrivateKey, + wallet *wallet.EphemeralWallet, + persistRestorer persistence.PersistRestorer, +) (*PaymentClient, error) { + backendRPCClient, err := rpc.Dial(rpcUrl) + if err != nil { + return nil, err + } + signer := backend.NewSignerInstance(address.AsParticipant(account.Address()).ToCKBAddress(network), key, network) + + ckbClient, err := ckbclient.NewClient(backendRPCClient, *signer, deployment) + if err != nil { + return nil, err + } + f := funder.NewDefaultFunder(ckbClient, deployment) + a := adjudicator.NewAdjudicator(ckbClient) + watcher, err := local.NewWatcher(a) + if err != nil { + return nil, err + } + wAddr := simple.NewAddress(account.Address().String()) + perunClient, err := client.New(wAddr, bus, f, a, wallet, watcher) + if err != nil { + return nil, err + } + perunClient.EnablePersistence(persistRestorer) + + balanceRPC, err := rpc.Dial(rpcUrl) + if err != nil { + return nil, err + } + p := &PaymentClient{ + Name: name, + balance: big.NewInt(0), + sudtBalance: big.NewInt(0), + Account: account, + wAddr: wAddr, + Network: network, + PerunClient: perunClient, + channels: make(chan *PaymentChannel, 1), + rpcClient: balanceRPC, + } + + //go p.PollBalances() + go perunClient.Handle(p, p) + return p, nil +} + +// WalletAddress returns the wallet address of the client. +func (p *PaymentClient) WalletAddress() gpwallet.Address { + return p.Account.Address() +} + +func (p *PaymentClient) WireAddress() wire.Address { + return p.wAddr +} + +func (p *PaymentClient) GetSudtBalance() *big.Int { + p.balanceMutex.Lock() + defer p.balanceMutex.Unlock() + return new(big.Int).Set(p.sudtBalance) +} + +// TODO: Remove as probably not required +/* +func (p *PaymentClient) NotifyAllBalance(ckbBal int64) string { + str := FormatBalance(new(big.Int).SetInt64(ckbBal), p.GetSudtBalance()) + return str +} +*/ + +// GetBalances retrieves the current balances of the client. +func (p *PaymentClient) GetBalances() string { + p.PollBalances() + return FormatBalance(p.balance, p.sudtBalance) +} + +// OpenChannel opens a new channel with the specified peer and funding. +func (p *PaymentClient) OpenChannel(peer wire.Address, amounts map[gpchannel.Asset]float64) *PaymentChannel { + // We define the channel participants. The proposer always has index 0. Here + // we use the on-chain addresses as off-chain addresses, but we could also + // use different ones. + log.Println("OpenChannel called") + participants := []wire.Address{p.WireAddress(), peer} + + assets := make([]gpchannel.Asset, len(amounts)) + i := 0 + for a := range amounts { + assets[i] = a + i++ + } + + // We create an initial allocation which defines the starting balances. + initAlloc := gpchannel.NewAllocation(2, assets...) + log.Println(initAlloc.Assets) + for a, amount := range amounts { + switch a := a.(type) { + case *asset.Asset: + if a.IsCKBytes { + initAlloc.SetAssetBalances(a, []gpchannel.Bal{ + CKByteToShannon(big.NewFloat(amount)), // Our initial balance. + CKByteToShannon(big.NewFloat(amount)), // Peer's initial balance. + }) + } else { + intAmount := new(big.Int).SetUint64(uint64(amount)) + initAlloc.SetAssetBalances(a, []gpchannel.Bal{ + intAmount, // Our initial balance. + intAmount, // Peer's initial balance. + }) + } + default: + panic("Asset is not of type *asset.Asset") + } + + } + log.Println("Created Allocation") + + // Prepare the channel proposal by defining the channel parameters. + challengeDuration := uint64(10) // On-chain challenge duration in seconds. + proposal, err := client.NewLedgerChannelProposal( + challengeDuration, + p.Account.Address(), + initAlloc, + participants, + ) + if err != nil { + panic(err) + } + + log.Println("Created Proposal") + + // Send the proposal. + ch, err := p.PerunClient.ProposeChannel(context.TODO(), proposal) + if err != nil { + panic(err) + } + + log.Println("Sent Channel") + + // Start the on-chain event watcher. It automatically handles disputes. + p.startWatching(ch) + + log.Println("Started Watching") + + //p.Channel = newPaymentChannel(ch, assets) + return newPaymentChannel(ch, assets) +} + +// startWatching starts the dispute watcher for the specified channel. +func (p *PaymentClient) startWatching(ch *client.Channel) { + go func() { + err := ch.Watch(p) + if err != nil { + fmt.Printf("Watcher returned with error: %v", err) + } + }() +} + +func (p *PaymentClient) AcceptedChannel() *PaymentChannel { + return <-p.channels +} + +func (p *PaymentClient) Shutdown() { + p.PerunClient.Close() +} + +func (c *PaymentClient) Restore() []*PaymentChannel { + var restoredChannels []*client.Channel + + //TODO: Remove this hack. Find why asset is not found upon restoring + c.PerunClient.OnNewChannel(func(ch *client.Channel) { + /* + state := ch.State().Clone() + ckbyte := asset.Asset{ + IsCKBytes: true, + SUDT: nil, + } + //create a new allocation where asset type is defined + alloc := gpchannel.NewAllocation(2, ckbyte) + ckbBalances := state.Allocation.Balances[0] + alloc.SetAssetBalances(ckbyte, ckbBalances) + */ + restoredChannels = append(restoredChannels, ch) + }) + + err := c.PerunClient.Restore(context.TODO()) + if err != nil { + fmt.Println("Error restoring channels") + } + + paymentChannels := make([]*PaymentChannel, len(restoredChannels)) + assets := make([]channel.Asset, 1) + assets = append(assets, &asset.Asset{ + IsCKBytes: true, + SUDT: nil, + }) + for i, ch := range restoredChannels { + paymentChannels[i] = newPaymentChannel(ch, assets) + } + + return paymentChannels +} diff --git a/payment-channel-ckb/client/handle.go b/payment-channel-ckb/client/handle.go new file mode 100644 index 0000000..b509ac1 --- /dev/null +++ b/payment-channel-ckb/client/handle.go @@ -0,0 +1,92 @@ +package client + +import ( + "context" + "fmt" + "log" + + "perun.network/go-perun/channel" + "perun.network/go-perun/client" +) + +// HandleProposal is the callback for incoming channel proposals. +func (p *PaymentClient) HandleProposal(prop client.ChannelProposal, r *client.ProposalResponder) { + lcp, err := func() (*client.LedgerChannelProposalMsg, error) { + // Ensure that we got a ledger channel proposal. + lcp, ok := prop.(*client.LedgerChannelProposalMsg) + if !ok { + return nil, fmt.Errorf("invalid proposal type: %T", p) + } + + // Check that we have the correct number of participants. + if lcp.NumPeers() != 2 { + return nil, fmt.Errorf("invalid number of participants: %d", lcp.NumPeers()) + } + // Check that the channel has the expected assets and funding balances. + for i, assetAlloc := range lcp.FundingAgreement { + if assetAlloc[0].Cmp(assetAlloc[1]) != 0 { + return nil, fmt.Errorf("invalid funding balance for asset %d: %v", i, assetAlloc) + } + + } + return lcp, nil + }() + if err != nil { + _ = r.Reject(context.TODO(), err.Error()) + } + + // Create a channel accept message and send it. + accept := lcp.Accept( + p.WalletAddress(), // The Account we use in the channel. + client.WithRandomNonce(), // Our share of the channel nonce. + ) + ch, err := r.Accept(context.TODO(), accept) + if err != nil { + log.Printf("Error accepting channel proposal: %v", err) + return + } + + //TODO: startWatching + // Start the on-chain event watcher. It automatically handles disputes. + p.startWatching(ch) + + // Store channel. + p.channels <- newPaymentChannel(ch, lcp.InitBals.Clone().Assets) + //p.AcceptedChannel() +} + +// HandleUpdate is the callback for incoming channel updates. +func (p *PaymentClient) HandleUpdate(cur *channel.State, next client.ChannelUpdate, r *client.UpdateResponder) { + // We accept every update that increases our balance. + err := func() error { + err := channel.AssertAssetsEqual(cur.Assets, next.State.Assets) + if err != nil { + return fmt.Errorf("invalid assets: %v", err) + } + + receiverIdx := 1 - next.ActorIdx // This works because we are in a two-party channel. + for _, a := range cur.Assets { + curBal := cur.Allocation.Balance(receiverIdx, a) + nextBal := next.State.Allocation.Balance(receiverIdx, a) + if nextBal.Cmp(curBal) < 0 { + return fmt.Errorf("invalid balance: %v", nextBal) + } + } + + return nil + }() + if err != nil { + _ = r.Reject(context.TODO(), err.Error()) + } + + // Send the acceptance message. + err = r.Accept(context.TODO()) + if err != nil { + panic(err) + } +} + +// HandleAdjudicatorEvent is the callback for smart contract events. +func (p *PaymentClient) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) { + log.Printf("Adjudicator event: type = %T, client = %v", e, p.Account) +} diff --git a/payment-channel-ckb/client/util.go b/payment-channel-ckb/client/util.go new file mode 100644 index 0000000..e0d8c11 --- /dev/null +++ b/payment-channel-ckb/client/util.go @@ -0,0 +1,22 @@ +package client + +import ( + "math/big" +) + +// CKByteToShannon converts a given amount in CKByte to Shannon. +func CKByteToShannon(ckbyteAmount *big.Float) (shannonAmount *big.Int) { + shannonPerCKByte := new(big.Int).Exp(big.NewInt(10), big.NewInt(8), nil) + shannonPerCKByteFloat := new(big.Float).SetInt(shannonPerCKByte) + shannonAmountFloat := new(big.Float).Mul(ckbyteAmount, shannonPerCKByteFloat) + shannonAmount, _ = shannonAmountFloat.Int(nil) + return shannonAmount +} + +// ShannonToCKByte converts a given amount in Shannon to CKByte. +func ShannonToCKByte(shannonAmount *big.Int) *big.Float { + shannonPerCKByte := new(big.Int).Exp(big.NewInt(10), big.NewInt(8), nil) + shannonPerCKByteFloat := new(big.Float).SetInt(shannonPerCKByte) + shannonAmountFloat := new(big.Float).SetInt(shannonAmount) + return new(big.Float).Quo(shannonAmountFloat, shannonPerCKByteFloat) +} diff --git a/payment-channel-ckb/deployment/deployment.go b/payment-channel-ckb/deployment/deployment.go new file mode 100644 index 0000000..376c050 --- /dev/null +++ b/payment-channel-ckb/deployment/deployment.go @@ -0,0 +1,159 @@ +package deployment + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "io" + "os" + "path" + "strings" + + "github.com/nervosnetwork/ckb-sdk-go/v2/types" + "perun.network/perun-ckb-backend/backend" +) + +const PFLSMinCapacity = 4100000032 + +type SUDTInfo struct { + Script *types.Script + CellDep *types.CellDep +} + +type Migration struct { + CellRecipes []struct { + Name string `json:"name"` + TxHash string `json:"tx_hash"` + Index uint32 `json:"index"` + OccupiedCapacity int64 `json:"occupied_capacity"` + DataHash string `json:"data_hash"` + TypeId interface{} `json:"type_id"` + } `json:"cell_recipes"` + DepGroupRecipes []interface{} `json:"dep_group_recipes"` +} + +func (m Migration) MakeDeployment(systemScripts SystemScripts, sudtOwnerLockArg string) (backend.Deployment, SUDTInfo, error) { + pcts := m.CellRecipes[0] + if pcts.Name != "pcts" { + return backend.Deployment{}, SUDTInfo{}, fmt.Errorf("first cell recipe must be pcts") + } + pcls := m.CellRecipes[1] + if pcls.Name != "pcls" { + return backend.Deployment{}, SUDTInfo{}, fmt.Errorf("second cell recipe must be pcls") + } + pfls := m.CellRecipes[2] + if pfls.Name != "pfls" { + return backend.Deployment{}, SUDTInfo{}, fmt.Errorf("third cell recipe must be pfls") + } + sudtInfo, err := m.GetSUDT() + if err != nil { + return backend.Deployment{}, SUDTInfo{}, err + } + // NOTE: The SUDT lock-arg always contains a newline character at the end. + hexString := strings.ReplaceAll(sudtOwnerLockArg[2:], "\n", "") + hexString = strings.ReplaceAll(hexString, "\r", "") + hexString = strings.ReplaceAll(hexString, " ", "") + sudtInfo.Script.Args, err = hex.DecodeString(hexString) + if err != nil { + return backend.Deployment{}, SUDTInfo{}, fmt.Errorf("invalid sudt owner lock arg: %v", err) + } + + return backend.Deployment{ + Network: types.NetworkTest, + PCTSDep: types.CellDep{ + OutPoint: &types.OutPoint{ + TxHash: types.HexToHash(pcts.TxHash), + Index: m.CellRecipes[0].Index, + }, + DepType: types.DepTypeCode, + }, + PCLSDep: types.CellDep{ + OutPoint: &types.OutPoint{ + TxHash: types.HexToHash(pcls.TxHash), + Index: m.CellRecipes[0].Index, + }, + DepType: types.DepTypeCode, + }, + PFLSDep: types.CellDep{ + OutPoint: &types.OutPoint{ + TxHash: types.HexToHash(pfls.TxHash), + Index: m.CellRecipes[0].Index, + }, + DepType: types.DepTypeCode, + }, + PCTSCodeHash: types.HexToHash(pcts.DataHash), + PCTSHashType: types.HashTypeData1, + PCLSCodeHash: types.HexToHash(pcls.DataHash), + PCLSHashType: types.HashTypeData1, + PFLSCodeHash: types.HexToHash(pfls.DataHash), + PFLSHashType: types.HashTypeData1, + PFLSMinCapacity: PFLSMinCapacity, + DefaultLockScript: types.Script{ + CodeHash: systemScripts.Secp256k1Blake160SighashAll.ScriptID.CodeHash, + HashType: systemScripts.Secp256k1Blake160SighashAll.ScriptID.HashType, + Args: make([]byte, 32), + }, + DefaultLockScriptDep: systemScripts.Secp256k1Blake160SighashAll.CellDep, + SUDTDeps: map[types.Hash]types.CellDep{ + sudtInfo.Script.Hash(): *sudtInfo.CellDep, + }, + SUDTs: map[types.Hash]types.Script{ + sudtInfo.Script.Hash(): *sudtInfo.Script, + }, + }, *sudtInfo, nil +} + +func (m Migration) GetSUDT() (*SUDTInfo, error) { + sudt := m.CellRecipes[3] + if sudt.Name != "sudt" { + return nil, fmt.Errorf("fourth cell recipe must be sudt") + } + + sudtScript := types.Script{ + CodeHash: types.HexToHash(sudt.DataHash), + HashType: types.HashTypeData1, + Args: []byte{}, + } + sudtCellDep := types.CellDep{ + OutPoint: &types.OutPoint{ + TxHash: types.HexToHash(sudt.TxHash), + Index: sudt.Index, + }, + DepType: types.DepTypeCode, + } + return &SUDTInfo{ + Script: &sudtScript, + CellDep: &sudtCellDep, + }, nil +} + +func GetDeployment(migrationDir, systemScriptsDir, sudtOwnerLockArg string) (backend.Deployment, SUDTInfo, error) { + dir, err := os.ReadDir(migrationDir) + if err != nil { + return backend.Deployment{}, SUDTInfo{}, err + } + if len(dir) != 1 { + return backend.Deployment{}, SUDTInfo{}, fmt.Errorf("migration dir must contain exactly one file") + } + migrationName := dir[0].Name() + migrationFile, err := os.Open(path.Join(migrationDir, migrationName)) + defer migrationFile.Close() + if err != nil { + return backend.Deployment{}, SUDTInfo{}, err + } + migrationData, err := io.ReadAll(migrationFile) + if err != nil { + return backend.Deployment{}, SUDTInfo{}, err + } + var migration Migration + err = json.Unmarshal(migrationData, &migration) + if err != nil { + return backend.Deployment{}, SUDTInfo{}, err + } + + ss, err := GetSystemScripts(systemScriptsDir) + if err != nil { + return backend.Deployment{}, SUDTInfo{}, err + } + return migration.MakeDeployment(ss, sudtOwnerLockArg) +} diff --git a/payment-channel-ckb/deployment/keys.go b/payment-channel-ckb/deployment/keys.go new file mode 100644 index 0000000..1d7d6bc --- /dev/null +++ b/payment-channel-ckb/deployment/keys.go @@ -0,0 +1,33 @@ +package deployment + +import ( + "encoding/hex" + "fmt" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "io" + "os" + "strings" +) + +func GetKey(path string) (*secp256k1.PrivateKey, error) { + keyFile, err := os.Open(path) + if err != nil { + return nil, err + } + defer keyFile.Close() + + rawBytes, err := io.ReadAll(keyFile) + if err != nil { + return nil, err + } + lines := strings.Split(string(rawBytes), "\n") + if len(lines) != 2 { + return nil, fmt.Errorf("key file must contain exactly two lines") + } + x := strings.Trim(lines[0], " \n") + xBytes, err := hex.DecodeString(x) + if err != nil { + return nil, err + } + return secp256k1.PrivKeyFromBytes(xBytes), nil +} diff --git a/payment-channel-ckb/deployment/system_scripts.go b/payment-channel-ckb/deployment/system_scripts.go new file mode 100644 index 0000000..77ee117 --- /dev/null +++ b/payment-channel-ckb/deployment/system_scripts.go @@ -0,0 +1,58 @@ +package deployment + +import ( + "encoding/json" + "io" + "os" + "path" + + "github.com/nervosnetwork/ckb-sdk-go/v2/types" +) + +type SystemScripts struct { + DAO struct { + CellDep types.CellDep `json:"cell_dep"` + ScriptID ScriptID `json:"script_id"` + } `json:"dao"` + Secp256k1Blake160MultisigAll struct { + CellDep types.CellDep `json:"cell_dep"` + ScriptID ScriptID `json:"script_id"` + } `json:"secp256k1_blake160_multisig_all"` + Secp256k1Blake160SighashAll struct { + CellDep types.CellDep `json:"cell_dep"` + ScriptID ScriptID `json:"script_id"` + } `json:"secp256k1_blake160_sighash_all"` + Secp256k1Data types.OutPoint `json:"secp256k1_data"` + TypeID struct { + ScriptID ScriptID `json:"script_id"` + } `json:"type_id"` +} + +type ScriptID struct { + CodeHash types.Hash `json:"code_hash"` + HashType types.ScriptHashType `json:"hash_type"` +} + +const systemScriptName = "default_scripts.json" + +func GetSystemScripts(systemScriptDir string) (SystemScripts, error) { + var ss SystemScripts + err := readJSON(systemScriptDir, &ss) + if err != nil { + return SystemScripts{}, err + } + return ss, nil +} + +func readJSON(systemScriptDir string, systemScripts *SystemScripts) error { + systemScriptFile, err := os.Open(path.Join(systemScriptDir, systemScriptName)) + defer func() { _ = systemScriptFile.Close() }() + if err != nil { + return err + } + systemScriptContent, err := io.ReadAll(systemScriptFile) + if err != nil { + return err + } + return json.Unmarshal(systemScriptContent, systemScripts) +} diff --git a/payment-channel-ckb/deployment/system_scripts_test.go b/payment-channel-ckb/deployment/system_scripts_test.go new file mode 100644 index 0000000..d7246a0 --- /dev/null +++ b/payment-channel-ckb/deployment/system_scripts_test.go @@ -0,0 +1,21 @@ +package deployment_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + "perun.network/perun-ckb-demo/deployment" +) + +func TestSystemScripts(t *testing.T) { + var ss deployment.SystemScripts + require.NoError(t, json.Unmarshal([]byte(systemScriptCase), &ss)) + msg, err := json.Marshal(ss) + require.NoError(t, err) + recovered := new(deployment.SystemScripts) + require.NoError(t, json.Unmarshal(msg, recovered)) + require.Equal(t, ss, *recovered) +} + +var systemScriptCase string = "{\"dao\":{\"cell_dep\":{\"dep_type\":\"code\",\"out_point\":{\"index\":\"0x2\",\"tx_hash\":\"0x297d19805fee99a53a6274a976df562d678beeff286776e1cd5ac9d8e1870780\"}},\"script_id\":{\"code_hash\":\"0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e\",\"hash_type\":\"type\"}},\"secp256k1_blake160_multisig_all\":{\"cell_dep\":{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x1\",\"tx_hash\":\"0xad69fbce31c6d8a8516789dec3cd4ddecbeb63619b4fa6cd3a7d00cdc788bf33\"}},\"script_id\":{\"code_hash\":\"0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8\",\"hash_type\":\"type\"}},\"secp256k1_blake160_sighash_all\":{\"cell_dep\":{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":\"0xad69fbce31c6d8a8516789dec3cd4ddecbeb63619b4fa6cd3a7d00cdc788bf33\"}},\"script_id\":{\"code_hash\":\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":\"type\"}},\"secp256k1_data\":{\"out_point\":{\"index\":\"0x3\",\"tx_hash\":\"0x297d19805fee99a53a6274a976df562d678beeff286776e1cd5ac9d8e1870780\"}},\"type_id\":{\"script_id\":{\"code_hash\":\"0x00000000000000000000000000000000000000000000000000545950455f4944\",\"hash_type\":\"type\"}}}" diff --git a/payment-channel-ckb/devnet/Makefile b/payment-channel-ckb/devnet/Makefile new file mode 100644 index 0000000..621fcad --- /dev/null +++ b/payment-channel-ckb/devnet/Makefile @@ -0,0 +1,2 @@ +dev: + tmuxp load ./devnet-session.yaml diff --git a/payment-channel-ckb/devnet/contracts/.assets/go-perun.png b/payment-channel-ckb/devnet/contracts/.assets/go-perun.png new file mode 100644 index 0000000000000000000000000000000000000000..6d8787bc738a1a711e97f8603987542675d6e3ee GIT binary patch literal 150688 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

8nZB^GeR(LCMaAb{OIE4WU)d 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

j?gU|`V_x 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 literal 0 HcmV?d00001 diff --git a/payment-channel-ckb/devnet/contracts/.github/workflows/rust.yml b/payment-channel-ckb/devnet/contracts/.github/workflows/rust.yml new file mode 100644 index 0000000..948acce --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/.github/workflows/rust.yml @@ -0,0 +1,38 @@ +name: Rust + +on: + push: + branches: [ "dev" ] + pull_request: + branches: [ "dev" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + tests/target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Capsule + run: cargo install --version 0.9.2 ckb-capsule + - name: Build perun-common + run: cargo build + working-directory: contracts/perun-common + - name: Test perun-common + run: cargo test + working-directory: contracts/perun-common + - name: Build contracts + run: capsule build + - name: Test contracts + run: capsule test diff --git a/payment-channel-ckb/devnet/contracts/.gitignore b/payment-channel-ckb/devnet/contracts/.gitignore new file mode 100644 index 0000000..8ce9103 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/.gitignore @@ -0,0 +1,14 @@ +# Rust +target/* +.cache/.cargo/* +contracts/**/target/* +tests/target/* +migrations/** + +# C +contracts/c/build/* + +# others +build/* +!.gitkeep +.tmp/ diff --git a/payment-channel-ckb/devnet/contracts/Cargo.lock b/payment-channel-ckb/devnet/contracts/Cargo.lock new file mode 100644 index 0000000..d54baf9 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/Cargo.lock @@ -0,0 +1,842 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "blake2b-ref" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95916998c798756098a4eb1b3f2cd510659705a9817bf203d61abd30fbec3e7b" + +[[package]] +name = "blake2b-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89a8565807f21b913288968e391819e7f9b2f0f46c7b89549c051cccf3a2771" +dependencies = [ + "cc", + "cty", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "buddy-alloc" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3240a4cb09cf0da6a51641bd40ce90e96ea6065e3a1adc46434029254bcc2d09" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ckb-channel" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "920f26cc48cadcaf6f7bcc3960fde9f9f355633b6361da8ef31e1e1c00fc8858" +dependencies = [ + "crossbeam-channel", +] + +[[package]] +name = "ckb-error" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446a519d8a847d97f1c8ece739dc1748751a9a2179249c96c45cced0825a7aa5" +dependencies = [ + "anyhow", + "ckb-occupied-capacity", + "derive_more", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00cbbc455b23748b32e06d16628a03e30d56ffa057f17093fdf5b42d4fb6c879" +dependencies = [ + "ckb-fixed-hash-core", + "ckb-fixed-hash-macros", +] + +[[package]] +name = "ckb-fixed-hash-core" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e644a4e026625b4be5a04cdf6c02043080e79feaf77d9cdbb2f0e6553f751" +dependencies = [ + "faster-hex", + "serde", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash-macros" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cfc980ef88c217825172eb46df269f47890f5e78a38214416f13b3bd17a4b4" +dependencies = [ + "ckb-fixed-hash-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-hash" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d9b683e89ae4ffdd5aaf4172eab00b6bbe7ea24e2abf77d3eb850ba36e8983" +dependencies = [ + "blake2b-ref", + "blake2b-rs", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ckb-occupied-capacity" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2a1dd0d4ba5dafba1e30d437c1148b20f42edb76b6794323e05bda626754eb" +dependencies = [ + "ckb-occupied-capacity-core", + "ckb-occupied-capacity-macros", +] + +[[package]] +name = "ckb-occupied-capacity-core" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ebba3d564098a84c83f4740e1dce48a5e2da759becdb47e3c7965f0808e6e92" +dependencies = [ + "serde", +] + +[[package]] +name = "ckb-occupied-capacity-macros" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6321bba85cdf9724029d8c906851dd4a90906869b42f9100b16645a1261d4c" +dependencies = [ + "ckb-occupied-capacity-core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-rational" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2519249f8d47fa758d3fb3cf3049327c69ce0f2acd79d61427482c8661d3dbd" +dependencies = [ + "numext-fixed-uint", + "serde", +] + +[[package]] +name = "ckb-standalone-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22d7cbbdab96e6b809a102cf88bfec28795a0a3c06bfdea4abe4de89777801cd" +dependencies = [ + "cfg-if", + "molecule", +] + +[[package]] +name = "ckb-std" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a6ad40455c446ad6fbb303dae24827fc309f43558f59d1f1b863a9de3e9f81" +dependencies = [ + "buddy-alloc", + "cc", + "ckb-standalone-types", + "cstr_core", +] + +[[package]] +name = "ckb-types" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c22b3b1ca8f88a8f48e2f73321c0605281c9c6f1e1c4d651c6138265c22291e" +dependencies = [ + "bit-vec", + "bytes", + "ckb-channel", + "ckb-error", + "ckb-fixed-hash", + "ckb-hash", + "ckb-merkle-mountain-range", + "ckb-occupied-capacity", + "ckb-rational", + "derive_more", + "merkle-cbt", + "molecule", + "numext-fixed-uint", + "once_cell", +] + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "cpufeatures" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cstr_core" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd98742e4fdca832d40cab219dc2e3048de17d873248f83f17df47c1bea70956" +dependencies = [ + "cty", + "memchr", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest", + "ff", + "generic-array", + "group", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "faster-hex" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "heapsize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +dependencies = [ + "winapi", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", + "sha3", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "merkle-cbt" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171d2f700835121c3b04ccf0880882987a050fd5c7ae88148abf537d33dd3a56" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "molecule" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc8276c02a006bddad7d1c28c1a88f30421e1b5f0ba0ca96ceb8077c7d20c01" +dependencies = [ + "bytes", + "cfg-if", + "faster-hex", +] + +[[package]] +name = "numext-constructor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "621fe0f044729f810c6815cdd77e8f5e0cd803ce4f6a38380ebfc1322af98661" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "numext-fixed-uint" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c68c76f96d589d1009a666c5072f37f3114d682696505f2cf445f27766c7d70" +dependencies = [ + "numext-fixed-uint-core", + "numext-fixed-uint-hack", +] + +[[package]] +name = "numext-fixed-uint-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aab1d6457b97b49482f22a92f0f58a2f39bdd7f3b2f977eae67e8bc206aa980" +dependencies = [ + "heapsize", + "numext-constructor", + "rand", + "serde", + "thiserror", +] + +[[package]] +name = "numext-fixed-uint-hack" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200f8d55c36ec1b6a8cf810115be85d4814f045e0097dfd50033ba25adb4c9e" +dependencies = [ + "numext-fixed-uint-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "perun-channel-lockscript" +version = "0.1.0" +dependencies = [ + "ckb-std", + "perun-common", +] + +[[package]] +name = "perun-channel-typescript" +version = "0.1.0" +dependencies = [ + "ckb-std", + "perun-common", +] + +[[package]] +name = "perun-common" +version = "0.1.0" +dependencies = [ + "blake2b-rs", + "buddy-alloc", + "ckb-occupied-capacity", + "ckb-standalone-types", + "ckb-std", + "ckb-types", + "k256", + "molecule", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "perun-funds-lockscript" +version = "0.1.0" +dependencies = [ + "ckb-std", + "perun-common", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff66d57013a5686e1917ed6a025d54dd591fcda71a41fe07edf4d16726aefa86" + +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c" + +[[package]] +name = "sample-udt" +version = "0.1.0" +dependencies = [ + "ckb-std", + "perun-common", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/payment-channel-ckb/devnet/contracts/Cargo.toml b/payment-channel-ckb/devnet/contracts/Cargo.toml new file mode 100644 index 0000000..bd68a6e --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = [ "contracts/perun-channel-lockscript" + , "contracts/perun-channel-typescript" + , "contracts/perun-funds-lockscript" + , "contracts/perun-common" + , "contracts/sample-udt" + ] +exclude = ["tests"] + +[profile.release] +overflow-checks = true +opt-level = 's' +lto = false +codegen-units = 1 +panic = 'abort' diff --git a/payment-channel-ckb/devnet/contracts/README.md b/payment-channel-ckb/devnet/contracts/README.md new file mode 100644 index 0000000..0f60dfb --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/README.md @@ -0,0 +1,45 @@ +


+ Perun +

+ +

Perun CKB Contracts

+ +

+ License: Apache 2.0 + CI status +

+ +# [Perun](https://perun.network/) CKB contracts + +This repository contains the scripts used to realize Perun channels on CKB. +There are three scripts available: + +## perun-channel-lockscript +This script is used to handle access-rights to the live Perun channel cell. +It ensures that only participants of the Perun channel in question are able to +consume the live channel cell. + +## perun-channel-typescript +This script is used to handle a Perun channel's state progression on-chain. +Basically a NFT script with extra functionality. + +## perun-funds-lockscript +This script handle access rights to all funds belonging to a Perun channel. +It ensures that only channel participants are able to consume said funds. + +Build contracts: + +``` sh +capsule build +``` + +Run tests: + +``` sh +capsule test +``` + +## perun-common +Additionally to the available contracts we extracted common functionality into +its own `perun-common` crate which gives some additional helpers and +convenience functions when interacting with types used in Perun contracts. diff --git a/payment-channel-ckb/devnet/contracts/build/.gitkeep b/payment-channel-ckb/devnet/contracts/build/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/payment-channel-ckb/devnet/contracts/capsule.toml b/payment-channel-ckb/devnet/contracts/capsule.toml new file mode 100644 index 0000000..a6549a9 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/capsule.toml @@ -0,0 +1,27 @@ +# [rust] +# # path of rust contracts workspace directory, +# # a `Cargo.toml` file is expected under the directory. +# workspace_dir = "." +# toolchain = "nightly-2022-08-01" +# docker_image = "thewawar/ckb-capsule:2022-08-01" + +# capsule version +version = "0.9.2" +# path of deployment config file +deployment = "deployment/dev/deployment.toml" + +[[contracts]] +name = "perun-channel-lockscript" +template_type = "Rust" + +[[contracts]] +name = "perun-channel-typescript" +template_type = "Rust" + +[[contracts]] +name = "perun-funds-lockscript" +template_type = "Rust" + +[[contracts]] +name = "sample-udt" +template_type = "Rust" diff --git a/payment-channel-ckb/devnet/contracts/contracts/.gitkeep b/payment-channel-ckb/devnet/contracts/contracts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/Cargo.toml b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/Cargo.toml new file mode 100644 index 0000000..049cf5b --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "perun-channel-lockscript" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ckb-std = "0.10.0" +perun-common = { path = "../perun-common", default-features = false, features = ["contract"] } diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/entry.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/entry.rs new file mode 100644 index 0000000..d8ea048 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/entry.rs @@ -0,0 +1,72 @@ +// Import from `core` instead of from `std` since we are in no-std mode +use core::result::Result; + +// Import CKB syscalls and structures +// https://docs.rs/ckb-std/ +use ckb_std::{ + ckb_constants::Source, + ckb_types::{bytes::Bytes, prelude::*}, + high_level::{load_cell_lock_hash, load_cell_type, load_script}, + syscalls::SysError, +}; +use perun_common::{error::Error, perun_types::ChannelConstants}; + +// The perun-channel-lockscript (pcls) is used to lock access to interacting with a channel and is attached as lock script +// to the channel-cell (the cell which uses the perun-channel-type-script (pcts) as its type script). +// A channel defines two participants, each of which has their own unlock_script_hash (also defined in the ChannelConstants.params.{party_a,party_b}). +// The pcls allows a transaction to interact with the channel, if at least one input cell is present with: +// - cell's lock script hash == unlock_script_hash of party_a or +// - cell's lock script hash == unlock_script_hash of party_b +// We recommend using the secp256k1_blake160_sighash_all script as unlock script and corresponding payment args for the participants. +// +// Note: This means, that each participant needs to use a secp256k1_blake160_sighash_all as input to interact with the channel. +// This should not be a substantial restriction, since a payment input will likely be used anyway (e.g. for funding or fees). + +pub fn main() -> Result<(), Error> { + let script = load_script()?; + let args: Bytes = script.args().unpack(); + // return an error if args is invalid + if !args.is_empty() { + return Err(Error::PCLSWithArgs); + } + + // locate the ChannelConstants in the type script of the input cell. + let type_script = load_cell_type(0, Source::GroupInput)?.expect("type script not found"); + let type_script_args: Bytes = type_script.args().unpack(); + + let constants = ChannelConstants::from_slice(&type_script_args) + .expect("unable to parse args as channel parameters"); + + let is_participant = verify_is_participant( + &constants.params().party_a().unlock_script_hash().unpack(), + &constants.params().party_b().unlock_script_hash().unpack(), + )?; + + if !is_participant { + return Err(Error::NotParticipant); + } + + return Ok(()); +} + +/// check_is_participant checks if the current transaction is executed by a channel participant. +/// It does so by looking for an input cell with the same lock script hash as the unlock_script_hash +pub fn verify_is_participant( + unlock_script_hash_a: &[u8; 32], + unlock_script_hash_b: &[u8; 32], +) -> Result { + for i in 0.. { + // Loop over all input cells. + let cell_lock_script_hash = match load_cell_lock_hash(i, Source::Input) { + Ok(lock_hash) => lock_hash, + Err(SysError::IndexOutOfBound) => return Ok(false), + Err(err) => return Err(err.into()), + }; + if cell_lock_script_hash[..] == unlock_script_hash_a[..] + || cell_lock_script_hash[..] == unlock_script_hash_b[..] + { + return Ok(true); + } + } + Ok(false) +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/main.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/main.rs new file mode 100644 index 0000000..9306fc4 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-lockscript/src/main.rs @@ -0,0 +1,32 @@ +//! Generated by capsule +//! +//! `main.rs` is used to define rust lang items and modules. +//! See `entry.rs` for the `main` function. +//! See `error.rs` for the `Error` type. + +#![no_std] +#![no_main] +#![feature(asm_sym)] +#![feature(lang_items)] +#![feature(alloc_error_handler)] +#![feature(panic_info_message)] + +// define modules +mod entry; + +use ckb_std::default_alloc; +use core::arch::asm; + +ckb_std::entry!(program_entry); +default_alloc!(); + +/// program entry +/// +/// Both `argc` and `argv` can be omitted. +fn program_entry(_argc: u64, _argv: *const *const u8) -> i8 { + // Call main function and return error code + match entry::main() { + Ok(_) => 0, + Err(err) => err as i8, + } +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/Cargo.toml b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/Cargo.toml new file mode 100644 index 0000000..85ccd63 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "perun-channel-typescript" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ckb-std = "0.10.0" +perun-common = { path = "../perun-common", default-features = false, features = ["contract"] } diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/entry.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/entry.rs new file mode 100644 index 0000000..e581e4d --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/entry.rs @@ -0,0 +1,904 @@ +// Import from `core` instead of from `std` since we are in no-std mode +use core::result::Result; +// Import heap related library from `alloc` +// https://doc.rust-lang.org/alloc/index.html +use alloc::{self, vec}; + +// Import CKB syscalls and structures +// https://docs.rs/ckb-std/ +use ckb_std::{ + ckb_constants::Source, + ckb_types::{ + bytes::Bytes, + packed::{Byte32, Script}, + prelude::*, + }, + debug, + high_level::{ + load_cell_capacity, load_cell_data, load_cell_lock, load_cell_lock_hash, load_header, + load_script, load_script_hash, load_transaction, load_witness_args, + }, + syscalls::{self, SysError}, +}; +use perun_common::{ + error::Error, + helpers::blake2b256, + perun_types::{ + Balances, ChannelConstants, ChannelParameters, ChannelState, ChannelStatus, ChannelToken, + ChannelWitness, ChannelWitnessUnion, SEC1EncodedPubKey, + }, + sig::verify_signature, +}; + +const SUDT_MIN_LEN: usize = 16; + +/// ChannelAction describes what kind of interaction with the channel is currently happening. +/// +/// If there is an old ChannelStatus, it is the status of the channel before the interaction. +/// The old ChannelStatus lives in the cell data of the pcts input cell. +/// It is stored in the parallel outputs_data array of the transaction that produced the consumed +/// channel output cell. +/// +/// If there is a new ChannelStatus, it is the status of the channel after the interaction. +/// The new ChannelStatus lives in the cell data of the pcts output cell. It is stored in the +/// parallel outputs_data array of the consuming transaction +pub enum ChannelAction { + /// Progress indicates that a channel is being progressed. This means that a channel cell is consumed + /// in the inputs and the same channel with updated state is progressed in the outputs. + /// The possible redeemers associated with the Progress action are Fund and Dispute. + Progress { + old_status: ChannelStatus, + new_status: ChannelStatus, + }, // one PCTS input, one PCTS output + /// Start indicates that a channel is being started. This means that a **new channel** lives in the + /// output cells of this transaction. No channel cell is consumes as an input. + /// As Start does not consume a channel cell, there is no Witness associated with the Start action. + Start { new_status: ChannelStatus }, // no PCTS input, one PCTS output + /// Close indicates that a channel is being closed. This means that a channel's cell is consumed without being + /// recreated in the outputs with updated state. The possible redeemers associated with the Close action are + /// Close, Abort and ForceClose. + /// The channel type script assures that all funds are payed out to the correct parties upon closing. + Close { old_status: ChannelStatus }, // one PCTS input , no PCTS output +} + +pub fn main() -> Result<(), Error> { + let script = load_script()?; + let args: Bytes = script.args().unpack(); + + // return an error if args is empty + if args.is_empty() { + return Err(Error::NoArgs); + } + + // We verify that there is at most one channel in the GroupInputs and GroupOutputs respectively. + verify_max_one_channel()?; + debug!("verify_max_one_channel passed"); + + // The channel constants do not change during the lifetime of a channel. They are located in the + // args field of the pcts. + let channel_constants = + ChannelConstants::from_slice(&args).expect("unable to parse args as ChannelConstants"); + debug!("parsing channel constants passed"); + + // Verify that the channel parameters are compatible with the currently supported + // features of perun channels. + verify_channel_params_compatibility(&channel_constants.params())?; + debug!("verify_channel_params_compatibility passed"); + + // Next, we determine whether the transaction starts, progresses or closes the channel and fetch + // the respective old and/or new channel status. + let channel_action = get_channel_action()?; + debug!("get_channel_action passed"); + + match channel_action { + ChannelAction::Start { new_status } => check_valid_start(&new_status, &channel_constants), + ChannelAction::Progress { + old_status, + new_status, + } => { + let channel_witness = load_witness()?; + debug!("load_witness passed"); + check_valid_progress( + &old_status, + &new_status, + &channel_witness, + &channel_constants, + ) + } + ChannelAction::Close { old_status } => { + let channel_witness = load_witness()?; + debug!("load_witness passed"); + check_valid_close(&old_status, &channel_witness, &channel_constants) + } + } +} + +pub fn check_valid_start( + new_status: &ChannelStatus, + channel_constants: &ChannelConstants, +) -> Result<(), Error> { + const FUNDER_INDEX: usize = 0; + + debug!("check_valid_start"); + + // Upon start of a channel, the channel constants are stored in the args field of the pcts output. + // We uniquely identify a channel through the combination of the channel id (hash of ChannelParameters, + // which is part of the ChannelConstants) and the "thread token". + // The thread token contains an OutPoint and the channel type script verifies, that that outpoint is + // consumed in the inputs of the transaction that starts the channel. + // This means: Once a (pcts-hash, channel-id, thread-token) tuple appears once on chain and is recognized + // as the on-chain representation of this channel by all peers, no other "copy" or "fake" of that channel + // can be created on chain, as an OutPoint can only be consumed once. + + // here, we verify that the OutPoint in the thread token is actually consumed. + verify_thread_token_integrity(&channel_constants.thread_token())?; + debug!("verify_thread_token_integrity passed"); + + // We verify that the channel id is the hash of the channel parameters. + verify_channel_id_integrity( + &new_status.state().channel_id(), + &channel_constants.params(), + )?; + debug!("verify_channel_id_integrity passed"); + + // We verify that the pcts is guarded by the pcls script specified in the channel constants + verify_valid_lock_script(channel_constants)?; + debug!("verify_valid_lock_script passed"); + + // We verify that the channel participants have different payment addresses + // For this purpose we consider a payment address to be the script hash of the lock script used for payments to that party + verify_different_payment_addresses(channel_constants)?; + debug!("verify_different_payment_addresses passed"); + + // We verify that there are no funds locked by the pfls hash of this channel in the inputs of the transaction. + // This check is not strictly necessary for the current implementation of the pfls, but it is good practice to + // verify this anyway, as there is no reason to include funds locked for any channel in the input of a transaction + // that creates a new channel besides trying some kind of attack. + verify_no_funds_in_inputs(channel_constants)?; + debug!("verify_no_funds_in_inputs passed"); + + // We verify that the state the channel starts with is valid according to the utxo-adaption of the perun protocol. + // For example, the channel must not be final and the version number must be 0. + verify_state_valid_as_start( + &new_status.state(), + channel_constants.pfls_min_capacity().unpack(), + )?; + debug!("verify_state_valid_as_start passed"); + + // Here we verify that the first party completes its funding and that itsfunds are actually locked to the pfls with correct args. + verify_funding_in_outputs( + FUNDER_INDEX, + &new_status.state().balances(), + channel_constants, + )?; + debug!("verify_funding_in_outputs passed"); + + // We check that the funded bit in the channel status is set to true, exactly if the funding is complete. + verify_funded_status(new_status, true)?; + debug!("verify_funded_status passed"); + + // We verify that the channel status is not disputed upon start. + verify_status_not_disputed(new_status)?; + debug!("verify_status_not_disputed passed"); + Ok(()) +} + +pub fn check_valid_progress( + old_status: &ChannelStatus, + new_status: &ChannelStatus, + witness: &ChannelWitness, + channel_constants: &ChannelConstants, +) -> Result<(), Error> { + debug!("check_valid_progress"); + + // At this point we know that the transaction progresses the channel. There are two different + // kinds of channel progression: Funding and Dispute. Which kind of progression is performed + // depends on the witness. + + // Some checks are common to both kinds of progression and are performed here. + // We check that both the old and the new state have the same channel id. + verify_equal_channel_id(&old_status.state(), &new_status.state())?; + debug!("verify_equal_channel_id passed"); + + // No kind of channel progression should pay out any funds locked by the pfls, so we just check + // that there are no funds locked by the pfls in the inputs of the transaction. + verify_no_funds_in_inputs(channel_constants)?; + debug!("verify_no_funds_in_inputs passed"); + // Here we verify that the cell with the PCTS in the outputs is locked by the same lock script + // as the input channel cell. + verify_channel_continues_locked()?; + debug!("verify_channel_continues_locked passed"); + + match witness.to_enum() { + ChannelWitnessUnion::Fund(_) => { + const FUNDER_INDEX: usize = 1; + debug!("ChannelWitnessUnion::Fund"); + + // The funding array in a channel status reflects how much each party has funded up to that point. + // Funding must not alter the channel's state. + verify_equal_channel_state(&old_status.state(), &new_status.state())?; + debug!("verify_equal_channel_state passed"); + + // Funding an already funded status is invalid. + verify_status_not_funded(&old_status)?; + debug!("verify_status_not_funded passed"); + + verify_funding_in_outputs( + FUNDER_INDEX, + &old_status.state().balances(), + channel_constants, + )?; + debug!("verify_funding_in_outputs passed"); + + // Funding a disputed status is invalid. This should not be able to happen anyway, but we check + // it nontheless. + verify_status_not_disputed(new_status)?; + debug!("verify_status_not_disputed passed"); + + // We check that the funded bit in the channel status is set to true, iff the funding is complete. + verify_funded_status(&new_status, false)?; + debug!("verify_funded_status passed"); + Ok(()) + } + ChannelWitnessUnion::Dispute(d) => { + debug!("ChannelWitnessUnion::Dispute"); + + // An honest party will dispute a channel, e.g. if its peer does not respond and it wants to close + // the channel. For this, the honest party needs to provide the latest state (in the "new" channel status) + // as well as a valid signature by each party on that state (in the witness). After the expiration of the + // relative time lock (challenge duration), the honest party can forcibly close the channel. + // If a malicious party disputes with an old channel state, an honest party can dispute again with + // the latest state (with higher version number) and the corresponding signatures within the challenge + // duration. + + // First, we verify the integrity of the channel state. For this, the following must hold: + // - channel id is equal + // - version number is increasing (see verify_increasing_version_number) + // - sum of balances is equal + // - old state is not final + verify_channel_state_progression(old_status, &new_status.state())?; + debug!("verify_channel_state_progression passed"); + + // One cannot dispute if funding is not complete. + verify_status_funded(old_status)?; + debug!("verify_status_funded passed"); + + // The disputed flag in the new status must be set. This indicates that the channel can be closed + // forcibly after the expiration of the challenge duration in a later transaction. + verify_status_disputed(new_status)?; + debug!("verify_status_disputed passed"); + + // We verify that the signatures of both parties are valid on the new channel state. + verify_valid_state_sigs( + &d.sig_a().unpack(), + &d.sig_b().unpack(), + &new_status.state(), + &channel_constants.params().party_a().pub_key(), + &channel_constants.params().party_b().pub_key(), + )?; + debug!("verify_valid_state_sigs passed"); + Ok(()) + } + // Close, ForceClose and Abort may not happen as channel progression (if there is a continuing channel output). + ChannelWitnessUnion::Close(_) => Err(Error::ChannelCloseWithChannelOutput), + ChannelWitnessUnion::ForceClose(_) => Err(Error::ChannelForceCloseWithChannelOutput), + ChannelWitnessUnion::Abort(_) => Err(Error::ChannelAbortWithChannelOutput), + } +} + +pub fn check_valid_close( + old_status: &ChannelStatus, + channel_witness: &ChannelWitness, + channel_constants: &ChannelConstants, +) -> Result<(), Error> { + debug!("check_valid_close"); + + // At this point we know that this transaction closes the channel. There are three different kinds of + // closing: Abort, ForceClose and Close. Which kind of closing is performed depends on the witness. + // Every channel closing transaction must pay out all funds the the channel participants. The amount + // to be payed to each party + let channel_capacity = load_cell_capacity(0, Source::GroupInput)?; + match channel_witness.to_enum() { + ChannelWitnessUnion::Abort(_) => { + const PARTY_B_INDEX: usize = 1; + + debug!("ChannelWitnessUnion::Abort"); + + // An abort can be performed at any time by a channel participant on a channel for which funding + // is not yet complete. It allows the initial party to reclaim its funds if e.g. the other party + // refuses to fund the channel. + verify_status_not_funded(old_status)?; + debug!("verify_status_not_funded passed"); + + // We verify that every party is payed the amount of funds that it has locked to the channel so far. + // If abourt is called, Party A must have fully funded the channel and Party B can not have funded + // the channel because of our funding protocol. + verify_all_payed( + &old_status.state().balances().clear_index(PARTY_B_INDEX)?, + channel_capacity, + channel_constants, + true, + )?; + debug!("verify_all_payed passed"); + Ok(()) + } + ChannelWitnessUnion::ForceClose(_) => { + debug!("ChannelWitnessUnion::ForceClose"); + // A force close can be performed after the channel was disputed and the challenge duration has + // expired. Upon force close, each party is payed according to the balance distribution in the + // latest state. + verify_status_funded(old_status)?; + debug!("verify_status_funded passed"); + verify_time_lock_expired(channel_constants.params().challenge_duration().unpack())?; + debug!("verify_time_lock_expired passed"); + verify_status_disputed(old_status)?; + debug!("verify_status_disputed passed"); + verify_all_payed( + &old_status.state().balances(), + channel_capacity, + channel_constants, + false, + )?; + debug!("verify_all_payed passed"); + Ok(()) + } + ChannelWitnessUnion::Close(c) => { + debug!("check_valid_close: Close"); + + // A channel can be closed by either party at any time after funding is complete. + // For this the party needs to provide a final state (final bit set) and signatures + // by all peers on that state. + verify_equal_channel_id(&old_status.state(), &c.state())?; + debug!("check_valid_close: Channel id verified"); + verify_status_funded(old_status)?; + debug!("check_valid_close: Status funded verified"); + verify_state_finalized(&c.state())?; + debug!("check_valid_close: State finalized verified"); + verify_valid_state_sigs( + &c.sig_a().unpack(), + &c.sig_b().unpack(), + &c.state(), + &channel_constants.params().party_a().pub_key(), + &channel_constants.params().party_b().pub_key(), + )?; + // We verify that each party is payed according to the balance distribution in the final state. + verify_all_payed( + &c.state().balances(), + channel_capacity, + channel_constants, + false, + )?; + debug!("verify_all_payed passed"); + Ok(()) + } + ChannelWitnessUnion::Fund(_) => Err(Error::ChannelFundWithoutChannelOutput), + ChannelWitnessUnion::Dispute(_) => Err(Error::ChannelDisputeWithoutChannelOutput), + } +} + +pub fn load_witness() -> Result { + debug!("load_witness"); + + let witness_args = load_witness_args(0, Source::GroupInput)?; + let witness_bytes: Bytes = witness_args + .input_type() + .to_opt() + .ok_or(Error::NoWitness)? + .unpack(); + let channel_witness = ChannelWitness::from_slice(&witness_bytes)?; + Ok(channel_witness) +} + +pub fn verify_increasing_version_number( + old_status: &ChannelStatus, + new_state: &ChannelState, +) -> Result<(), Error> { + debug!( + "verify_increasing_version_number old_state disputed: {}", + old_status.disputed().to_bool() + ); + debug!( + "verify_increasing_version_number old: {}, new: {}", + old_status.state().version().unpack(), + new_state.version().unpack() + ); + // Allow registering initial state + if !old_status.disputed().to_bool() + && old_status.state().version().unpack() == 0 + && new_state.version().unpack() == 0 + { + return Ok(()); + } + if old_status.state().version().unpack() < new_state.version().unpack() { + return Ok(()); + } + Err(Error::VersionNumberNotIncreasing) +} + +pub fn verify_valid_state_sigs( + sig_a: &Bytes, + sig_b: &Bytes, + state: &ChannelState, + pub_key_a: &SEC1EncodedPubKey, + pub_key_b: &SEC1EncodedPubKey, +) -> Result<(), Error> { + let msg_hash = blake2b256(state.as_slice()); + verify_signature(&msg_hash, sig_a, pub_key_a.as_slice())?; + debug!("verify_valid_state_sigs: Signature A verified"); + verify_signature(&msg_hash, sig_b, pub_key_b.as_slice())?; + debug!("verify_valid_state_sigs: Signature B verified"); + Ok(()) +} + +pub fn verify_state_not_finalized(state: &ChannelState) -> Result<(), Error> { + if state.is_final().to_bool() { + return Err(Error::StateIsFinal); + } + Ok(()) +} + +pub fn verify_status_funded(status: &ChannelStatus) -> Result<(), Error> { + if !status.funded().to_bool() { + return Err(Error::ChannelNotFunded); + } + Ok(()) +} + +pub fn verify_equal_sum_of_balances( + old_balances: &Balances, + new_balances: &Balances, +) -> Result<(), Error> { + if !old_balances.equal_in_sum(new_balances)? { + return Err(Error::SumOfBalancesNotEqual); + } + Ok(()) +} + +pub fn verify_channel_continues_locked() -> Result<(), Error> { + let input_lock_script = load_cell_lock(0, Source::GroupInput)?; + let output_lock_script = load_cell_lock(0, Source::GroupOutput)?; + if input_lock_script.as_slice()[..] != output_lock_script.as_slice()[..] { + return Err(Error::ChannelDoesNotContinue); + } + Ok(()) +} + +pub fn verify_no_funds_in_inputs(channel_constants: &ChannelConstants) -> Result<(), Error> { + let num_inputs = load_transaction()?.raw().inputs().len(); + for i in 0..num_inputs { + let cell_lock_hash = load_cell_lock(i, Source::Input)?; + if cell_lock_hash.code_hash().unpack()[..] + == channel_constants.pfls_code_hash().unpack()[..] + { + return Err(Error::FundsInInputs); + } + } + Ok(()) +} + +pub fn verify_equal_channel_state( + old_state: &ChannelState, + new_state: &ChannelState, +) -> Result<(), Error> { + if old_state.as_slice()[..] == new_state.as_slice()[..] { + return Ok(()); + } + Err(Error::ChannelStateNotEqual) +} + +pub fn verify_funding_in_outputs( + idx: usize, + initial_balance: &Balances, + channel_constants: &ChannelConstants, +) -> Result<(), Error> { + let ckbytes_locked_for_sudts = initial_balance.sudts().get_locked_ckbytes(); + let to_fund = initial_balance.ckbytes().get(idx)? + ckbytes_locked_for_sudts; + if to_fund == 0 { + return Ok(()); + } + + let mut udt_sum = + vec![0u128, initial_balance.sudts().len().try_into().unwrap()].into_boxed_slice(); + + let expected_pcts_script_hash = load_script_hash()?; + let outputs = load_transaction()?.raw().outputs(); + let expected_pfls_code_hash = channel_constants.pfls_code_hash().unpack(); + let expected_pfls_hash_type = channel_constants.pfls_hash_type(); + let mut capacity_sum: u64 = 0; + for (i, output) in outputs.into_iter().enumerate() { + if output.lock().code_hash().unpack()[..] == expected_pfls_code_hash[..] + && output.lock().hash_type().eq(&expected_pfls_hash_type) + { + let output_lock_args: Bytes = output.lock().args().unpack(); + let script_hash_in_pfls_args = Byte32::from_slice(&output_lock_args)?.unpack(); + if script_hash_in_pfls_args[..] == expected_pcts_script_hash[..] { + capacity_sum += output.capacity().unpack(); + } else { + return Err(Error::InvalidPFLSInOutputs); + } + if output.type_().is_some() { + let (sudt_idx, amount) = get_sudt_amout( + initial_balance, + i, + &output.type_().to_opt().expect("checked above"), + )?; + udt_sum[sudt_idx] += amount; + } + } + } + if capacity_sum != to_fund { + debug!( + "verify_funding_in_outputs: capacity_sum: {}, to_fund: {}", + capacity_sum, to_fund + ); + return Err(Error::OwnFundingNotInOutputs); + } + if !initial_balance.sudts().fully_represented(idx, &udt_sum)? { + return Err(Error::OwnFundingNotInOutputs); + } + + Ok(()) +} + +pub fn verify_funded_status(status: &ChannelStatus, is_start: bool) -> Result<(), Error> { + if !is_start { + if !status.funded().to_bool() { + return Err(Error::FundedBitStatusNotCorrect); + } + return Ok(()); + } + if status.state().balances().ckbytes().get(1)? != 0 { + if status.funded().to_bool() { + return Err(Error::FundedBitStatusNotCorrect); + } + return Ok(()); + } + if status.state().balances().sudts().len() != 0 { + if status.funded().to_bool() { + return Err(Error::FundedBitStatusNotCorrect); + } + return Ok(()); + } + if !status.funded().to_bool() { + return Err(Error::FundedBitStatusNotCorrect); + } + Ok(()) +} + +pub fn verify_status_not_funded(status: &ChannelStatus) -> Result<(), Error> { + if status.funded().to_bool() { + return Err(Error::StateIsFunded); + } + Ok(()) +} + +pub fn verify_channel_params_compatibility(params: &ChannelParameters) -> Result<(), Error> { + if params.app().to_opt().is_some() { + return Err(Error::AppChannelsNotSupported); + } + if !params.is_ledger_channel().to_bool() { + return Err(Error::NonLedgerChannelsNotSupported); + } + if params.is_virtual_channel().to_bool() { + return Err(Error::VirtualChannelsNotSupported); + } + Ok(()) +} + +pub fn verify_equal_channel_id( + old_state: &ChannelState, + new_state: &ChannelState, +) -> Result<(), Error> { + if old_state.channel_id().unpack()[..] != new_state.channel_id().unpack()[..] { + return Err(Error::ChannelIdMismatch); + } + Ok(()) +} + +pub fn verify_channel_state_progression( + old_status: &ChannelStatus, + new_state: &ChannelState, +) -> Result<(), Error> { + verify_equal_channel_id(&old_status.state(), new_state)?; + verify_increasing_version_number(old_status, new_state)?; + verify_equal_sum_of_balances(&old_status.state().balances(), &new_state.balances())?; + verify_state_not_finalized(&old_status.state())?; + Ok(()) +} + +pub fn verify_thread_token_integrity(thread_token: &ChannelToken) -> Result<(), Error> { + let inputs = load_transaction()?.raw().inputs(); + for input in inputs.into_iter() { + if input.previous_output().as_slice()[..] == thread_token.out_point().as_slice()[..] { + return Ok(()); + } + } + Err(Error::InvalidThreadToken) +} + +pub fn verify_channel_id_integrity( + channel_id: &Byte32, + params: &ChannelParameters, +) -> Result<(), Error> { + let digest = blake2b256(params.as_slice()); + if digest[..] != channel_id.unpack()[..] { + return Err(Error::InvalidChannelId); + } + Ok(()) +} + +pub fn verify_state_valid_as_start( + state: &ChannelState, + pfls_min_capacity: u64, +) -> Result<(), Error> { + if state.version().unpack() != 0 { + return Err(Error::StartWithNonZeroVersion); + } + if state.is_final().to_bool() { + return Err(Error::StartWithFinalizedState); + } + + // We verify that each participant's initial balance is at least the minimum capacity of a PFLS (or zero), + // to ensure that funding is possible for the initial balance distribution. + let balance_a = state.balances().ckbytes().get(0)?; + let balance_b = state.balances().ckbytes().get(1)?; + if balance_a < pfls_min_capacity && balance_a != 0 { + return Err(Error::BalanceBelowPFLSMinCapacity); + } + if balance_b < pfls_min_capacity && balance_b != 0 { + return Err(Error::BalanceBelowPFLSMinCapacity); + } + Ok(()) +} + +pub fn verify_valid_lock_script(channel_constants: &ChannelConstants) -> Result<(), Error> { + let lock_script = load_cell_lock(0, Source::GroupOutput)?; + if lock_script.code_hash().unpack()[..] != channel_constants.pcls_code_hash().unpack()[..] { + return Err(Error::InvalidPCLSCodeHash); + } + if !lock_script + .hash_type() + .eq(&channel_constants.pcls_hash_type()) + { + return Err(Error::InvalidPCLSHashType); + } + + if !lock_script.args().is_empty() { + return Err(Error::PCLSWithArgs); + } + Ok(()) +} + +pub fn verify_status_not_disputed(status: &ChannelStatus) -> Result<(), Error> { + if status.disputed().to_bool() { + return Err(Error::StatusDisputed); + } + Ok(()) +} + +pub fn verify_status_disputed(status: &ChannelStatus) -> Result<(), Error> { + if !status.disputed().to_bool() { + return Err(Error::StatusNotDisputed); + } + Ok(()) +} + +pub fn verify_all_payed( + final_balance: &Balances, + channel_capacity: u64, + channel_constants: &ChannelConstants, + is_abort: bool, +) -> Result<(), Error> { + debug!("verify_all_payed"); + debug!("is_abort: {}", is_abort); + let minimum_payment_a = channel_constants + .params() + .party_a() + .payment_min_capacity() + .unpack(); + let minimum_payment_b: u64 = channel_constants + .params() + .party_b() + .payment_min_capacity() + .unpack(); + + let reimburse_a = final_balance.sudts().get_locked_ckbytes(); + let mut reimburse_b = 0u64; + if !is_abort { + reimburse_b = reimburse_a; + } + + let ckbytes_balance_a = final_balance.ckbytes().get(0)? + channel_capacity + reimburse_a; + let payment_script_hash_a = channel_constants + .params() + .party_a() + .payment_script_hash() + .unpack(); + + let ckbytes_balance_b = final_balance.ckbytes().get(1)? + reimburse_b; + let payment_script_hash_b = channel_constants + .params() + .party_b() + .payment_script_hash() + .unpack(); + + debug!("ckbytes_balance_a: {}", ckbytes_balance_a); + debug!("ckbytes_balance_b: {}", ckbytes_balance_b); + + let mut ckbytes_outputs_a = 0; + let mut ckbytes_outputs_b = 0; + + let mut udt_outputs_a = + vec![0u128; final_balance.sudts().len().try_into().unwrap()].into_boxed_slice(); + let mut udt_outputs_b = + vec![0u128; final_balance.sudts().len().try_into().unwrap()].into_boxed_slice(); + + let outputs = load_transaction()?.raw().outputs(); + + // Note: Currently it is allowed to pay out a party's CKBytes in the capacity field of an + // output, that is used as SUDT payment. + for (i, output) in outputs.into_iter().enumerate() { + let output_lock_script_hash = load_cell_lock_hash(i, Source::Output)?; + + if output_lock_script_hash[..] == payment_script_hash_a[..] { + if output.type_().is_some() { + let (sudt_idx, amount) = get_sudt_amout( + final_balance, + i, + &output.type_().to_opt().expect("checked above"), + )?; + udt_outputs_a[sudt_idx] += amount; + } + ckbytes_outputs_a += output.capacity().unpack(); + } + if output_lock_script_hash[..] == payment_script_hash_b[..] { + if output.type_().is_some() { + let (sudt_idx, amount) = get_sudt_amout( + final_balance, + i, + &output.type_().to_opt().expect("checked above"), + )?; + udt_outputs_b[sudt_idx] += amount; + } + ckbytes_outputs_b += output.capacity().unpack(); + } + } + debug!("ckbytes_outputs_a: {}", ckbytes_outputs_a); + debug!("ckbytes_outputs_b: {}", ckbytes_outputs_b); + + // Parties with balances below the minimum capacity of the payment script + // are not required to be payed. + if (ckbytes_balance_a > ckbytes_outputs_a && ckbytes_balance_a >= minimum_payment_a) + || (ckbytes_balance_b > ckbytes_outputs_b && ckbytes_balance_b >= minimum_payment_b) + { + return Err(Error::NotAllPayed); + } + + debug!("udt_outputs_a: {:?}", udt_outputs_a); + debug!("udt_outputs_b: {:?}", udt_outputs_b); + + if !final_balance.sudts().fully_represented(0, &udt_outputs_a)? { + return Err(Error::NotAllPayed); + } + if !final_balance.sudts().fully_represented(1, &udt_outputs_b)? { + return Err(Error::NotAllPayed); + } + Ok(()) +} + +// TODO: We might want to verify that the capacity of the sudt output is at least the max_capacity of the SUDT asset. +// Not doing so may result in the ability to steal funds up to the +// (max_capacity of the SUDT asset - actual occupied capacity of the SUDT type script), if the SUDT asset's max_capacity +// is smaller than the payment_min_capacity of the participant. We do not do this for now, because it is an extreme edge case +// and the max_capacity of an SUDT should never be set that low. +pub fn get_sudt_amout( + balances: &Balances, + output_idx: usize, + type_script: &Script, +) -> Result<(usize, u128), Error> { + let mut buf = [0u8; SUDT_MIN_LEN]; + + let (sudt_idx, _) = balances.sudts().get_distribution(type_script)?; + let sudt_data = load_cell_data(output_idx, Source::Output)?; + if sudt_data.len() < SUDT_MIN_LEN { + return Err(Error::InvalidSUDTDataLength); + } + buf.copy_from_slice(&sudt_data[..SUDT_MIN_LEN]); + return Ok((sudt_idx, u128::from_le_bytes(buf))); +} + +pub fn verify_time_lock_expired(time_lock: u64) -> Result<(), Error> { + let old_header = load_header(0, Source::GroupInput)?; + let old_timestamp = old_header.raw().timestamp().unpack(); + let current_time = find_closest_current_time(); + if old_timestamp + time_lock > current_time { + return Err(Error::TimeLockNotExpired); + } + Ok(()) +} + +pub fn find_closest_current_time() -> u64 { + let mut latest_time = 0; + for i in 0.. { + match load_header(i, Source::HeaderDep) { + Ok(header) => { + let timestamp = header.raw().timestamp().unpack(); + if timestamp > latest_time { + latest_time = timestamp; + } + } + Err(_) => break, + } + } + latest_time +} + +pub fn verify_state_finalized(state: &ChannelState) -> Result<(), Error> { + if !state.is_final().to_bool() { + return Err(Error::StateNotFinal); + } + Ok(()) +} + +pub fn get_channel_action() -> Result { + let input_status_opt = load_cell_data(0, Source::GroupInput) + .ok() + .map(|data| ChannelStatus::from_slice(data.as_slice())) + .map_or(Ok(None), |v| v.map(Some))?; + + let output_status_opt = load_cell_data(0, Source::GroupOutput) + .ok() + .map(|data| ChannelStatus::from_slice(data.as_slice())) + .map_or(Ok(None), |v| v.map(Some))?; + + match (input_status_opt, output_status_opt) { + (Some(old_status), Some(new_status)) => Ok(ChannelAction::Progress { + old_status, + new_status, + }), + (Some(old_status), None) => Ok(ChannelAction::Close { old_status }), + (None, Some(new_status)) => Ok(ChannelAction::Start { new_status }), + (None, None) => Err(Error::UnableToLoadAnyChannelStatus), + } +} + +/// verify_max_one_channel verifies that there is at most one channel in the group input and group output respectively. +pub fn verify_max_one_channel() -> Result<(), Error> { + if count_cells(Source::GroupInput)? > 1 || count_cells(Source::GroupOutput)? > 1 { + return Err(Error::MoreThanOneChannel); + } else { + return Ok(()); + } +} + +pub fn count_cells(source: Source) -> Result { + let mut null_buf: [u8; 0] = []; + for i in 0.. { + match syscalls::load_cell(&mut null_buf, 0, i, source) { + Ok(_) => continue, + Err(SysError::LengthNotEnough(_)) => continue, + Err(SysError::IndexOutOfBound) => return Ok(i), + Err(err) => return Err(err.into()), + } + } + Ok(0) +} + +pub fn verify_different_payment_addresses( + channel_constants: &ChannelConstants, +) -> Result<(), Error> { + if channel_constants + .params() + .party_a() + .payment_script_hash() + .unpack()[..] + == channel_constants + .params() + .party_b() + .payment_script_hash() + .unpack()[..] + { + return Err(Error::SamePaymentAddress); + } + Ok(()) +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/main.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/main.rs new file mode 100644 index 0000000..9306fc4 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-channel-typescript/src/main.rs @@ -0,0 +1,32 @@ +//! Generated by capsule +//! +//! `main.rs` is used to define rust lang items and modules. +//! See `entry.rs` for the `main` function. +//! See `error.rs` for the `Error` type. + +#![no_std] +#![no_main] +#![feature(asm_sym)] +#![feature(lang_items)] +#![feature(alloc_error_handler)] +#![feature(panic_info_message)] + +// define modules +mod entry; + +use ckb_std::default_alloc; +use core::arch::asm; + +ckb_std::entry!(program_entry); +default_alloc!(); + +/// program entry +/// +/// Both `argc` and `argv` can be omitted. +fn program_entry(_argc: u64, _argv: *const *const u8) -> i8 { + // Call main function and return error code + match entry::main() { + Ok(_) => 0, + Err(err) => err as i8, + } +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/Cargo.toml b/payment-channel-ckb/devnet/contracts/contracts/perun-common/Cargo.toml new file mode 100644 index 0000000..fb7ec7b --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "perun-common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ckb-std = "0.10.0" +blake2b-rs = "0.2.0" +ckb-standalone-types = { version = "0.1.2", default-features = false, optional = true } +ckb-types = { version = "=0.108.0", optional = true } +k256 = { version = "0.11.6", default-features = false, features = ["ecdsa", "keccak256", "arithmetic"]} +alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" } +core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } +buddy-alloc = { version = "0.4.2", optional = true } +ckb-occupied-capacity = { version = "0.108.0", optional = true } + +[dependencies.molecule] +version = "0.7.3" +default-features = false + +[features] +default = ["contract"] +testing = ["std", "ckb-types", "ckb-occupied-capacity"] +std = [] +contract = ["ckb-standalone-types"] diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/blockchain.mol b/payment-channel-ckb/devnet/contracts/contracts/perun-common/blockchain.mol new file mode 100644 index 0000000..091441c --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/blockchain.mol @@ -0,0 +1,108 @@ +/* Basic Types */ + +array Uint32 [byte; 4]; +array Uint64 [byte; 8]; +array Uint128 [byte; 16]; +array Byte32 [byte; 32]; +array Uint256 [byte; 32]; + +vector Bytes ; +option BytesOpt (Bytes); + +vector BytesVec ; +vector Byte32Vec ; + +/* Types for Chain */ + +option ScriptOpt (Script); + +array ProposalShortId [byte; 10]; + +vector UncleBlockVec ; +vector TransactionVec ; +vector ProposalShortIdVec ; +vector CellDepVec ; +vector CellInputVec ; +vector CellOutputVec ; + +table Script { + code_hash: Byte32, + hash_type: byte, + args: Bytes, +} + +struct OutPoint { + tx_hash: Byte32, + index: Uint32, +} + +struct CellInput { + since: Uint64, + previous_output: OutPoint, +} + +table CellOutput { + capacity: Uint64, + lock: Script, + type_: ScriptOpt, +} + +struct CellDep { + out_point: OutPoint, + dep_type: byte, +} + +table RawTransaction { + version: Uint32, + cell_deps: CellDepVec, + header_deps: Byte32Vec, + inputs: CellInputVec, + outputs: CellOutputVec, + outputs_data: BytesVec, +} + +table Transaction { + raw: RawTransaction, + witnesses: BytesVec, +} + +struct RawHeader { + version: Uint32, + compact_target: Uint32, + timestamp: Uint64, + number: Uint64, + epoch: Uint64, + parent_hash: Byte32, + transactions_root: Byte32, + proposals_hash: Byte32, + uncles_hash: Byte32, + dao: Byte32, +} + +struct Header { + raw: RawHeader, + nonce: Uint128, +} + +table UncleBlock { + header: Header, + proposals: ProposalShortIdVec, +} + +table Block { + header: Header, + uncles: UncleBlockVec, + transactions: TransactionVec, + proposals: ProposalShortIdVec, +} + +table CellbaseWitness { + lock: Script, + message: Bytes, +} + +table WitnessArgs { + lock: BytesOpt, // Lock args + input_type: BytesOpt, // Type args for input + output_type: BytesOpt, // Type args for output +} \ No newline at end of file diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/offchain_types.mol b/payment-channel-ckb/devnet/contracts/contracts/perun-common/offchain_types.mol new file mode 100644 index 0000000..ba4758e --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/offchain_types.mol @@ -0,0 +1,8 @@ +import blockchain; +import types; + +table OffChainParticipant { + pub_key: SEC1EncodedPubKey, + payment_script: Script, + unlock_script: Script, +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/error.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/error.rs new file mode 100644 index 0000000..c40a0e7 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/error.rs @@ -0,0 +1,115 @@ +use core::fmt::Debug; + +use ckb_std::error::SysError; +use k256::ecdsa::Error as SigError; +use molecule::error::VerificationError; + +/// Error +#[derive(Debug)] +#[repr(i8)] +pub enum Error { + // System Errors + IndexOutOfBound = 1, + ItemMissing, + LengthNotEnough, + Encoding, + // Verification Errors + TotalSizeNotMatch, + HeaderIsBroken, + UnknownItem, + OffsetsNotMatch, + FieldCountNotMatch, + + // Signature Errors + SignatureVerificationError, + + // Add customized errors here... + NoArgs, + NoWitness, + ChannelIdMismatch, + VersionNumberNotIncreasing, + StateIsFinal, + StateNotFinal, + ChannelNotFunded, + NotParticipant, + SumOfBalancesNotEqual, + OwnIndexNotFound, + ChannelDoesNotContinue, + MultipleMatchingOutputs, + FundsInInputs, + AppChannelsNotSupported, + NonLedgerChannelsNotSupported, + VirtualChannelsNotSupported, + ChannelStateNotEqual, + FundingChanged, + FundingNotInStatus, + OwnFundingNotInOutputs, + FundedBitStatusNotCorrect, + StateIsFunded, + + ChannelFundWithoutChannelOutput, + ChannelDisputeWithoutChannelOutput, + ChannelCloseWithChannelOutput, + ChannelForceCloseWithChannelOutput, + ChannelAbortWithChannelOutput, + + InvalidThreadToken, + InvalidChannelId, + StartWithNonZeroVersion, + StartWithFinalizedState, + InvalidPCLSCodeHash, + InvalidPCLSHashType, + PCLSWithArgs, + StatusDisputed, + StatusNotDisputed, + FundingNotZero, + NotAllPayed, + TimeLockNotExpired, + InvalidTimestamp, + UnableToLoadAnyChannelStatus, + InvalidSignature, + InvalidMessage, + InvalidPFLSInOutputs, + PCTSNotFound, + FoundDifferentChannel, + MoreThanOneChannel, + BalanceBelowPFLSMinCapacity, + SamePaymentAddress, + TypeScriptInPaymentOutput, + TypeScriptInPFLSOutput, + InvalidSUDT, + InvalidSUDTDataLength, + DecreasingAmount, +} + +impl From for Error { + fn from(err: SysError) -> Self { + use SysError::*; + match err { + IndexOutOfBound => Self::IndexOutOfBound, + ItemMissing => Self::ItemMissing, + LengthNotEnough(_) => Self::LengthNotEnough, + Encoding => Self::Encoding, + Unknown(err_code) => panic!("unexpected sys error {}", err_code), + } + } +} + +impl From for Error { + fn from(err: VerificationError) -> Self { + use VerificationError::*; + match err { + TotalSizeNotMatch(_, _, _) => Self::TotalSizeNotMatch, + HeaderIsBroken(_, _, _) => Self::HeaderIsBroken, + UnknownItem(_, _, _) => Self::UnknownItem, + OffsetsNotMatch(_) => Self::OffsetsNotMatch, + FieldCountNotMatch(_, _, _) => Self::FieldCountNotMatch, + } + } +} + +impl From for Error { + fn from(_: SigError) -> Self { + return Self::SignatureVerificationError; + } +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/helpers.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/helpers.rs new file mode 100644 index 0000000..e5f6e79 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/helpers.rs @@ -0,0 +1,448 @@ +use blake2b_rs::Blake2bBuilder; + +#[cfg(feature = "std")] +use { + crate::perun_types::ChannelState, ckb_types::bytes, ckb_types::packed::*, + ckb_types::prelude::*, std::vec::Vec, +}; + +#[cfg(not(feature = "std"))] +use { + ckb_standalone_types::packed::*, + ckb_standalone_types::prelude::*, + molecule::prelude::{vec, Vec}, +}; + +use crate::perun_types::{ + Balances, Bool, BoolUnion, ChannelParameters, ChannelStatus, SEC1EncodedPubKey, +}; +use crate::{ + error::Error, + perun_types::{CKByteDistribution, SUDTAllocation, SUDTBalances, SUDTDistribution}, +}; + +impl Bool { + pub fn to_bool(&self) -> bool { + match self.to_enum() { + BoolUnion::True(_) => true, + BoolUnion::False(_) => false, + } + } + pub fn from_bool(b: bool) -> Self { + if b { + return ctrue!(); + } else { + return cfalse!(); + } + } +} + +#[macro_export] +macro_rules! ctrue { + () => { + $crate::perun_types::BoolBuilder::default() + .set($crate::perun_types::BoolUnion::True( + $crate::perun_types::True::default(), + )) + .build() + }; +} +pub(crate) use ctrue; + +#[macro_export] +macro_rules! cfalse { + () => { + $crate::perun_types::BoolBuilder::default() + .set($crate::perun_types::BoolUnion::False( + $crate::perun_types::False::default(), + )) + .build() + }; +} +pub(crate) use cfalse; + +#[macro_export] +macro_rules! redeemer { + ($name:ident) => { + $crate::perun_types::ChannelWitnessBuilder::default() + .set($crate::perun_types::ChannelWitnessUnion::$name( + Default::default(), + )) + .build() + }; + ($x:expr) => { + $crate::perun_types::ChannelWitnessBuilder::default() + .set($x) + .build() + }; +} + +#[macro_export] +macro_rules! fund { + () => { + $crate::perun_types::ChannelWitnessUnion::Fund($crate::perun_types::Fund::default()) + }; +} + +#[macro_export] +macro_rules! close { + ($state:expr, $siga:expr, $sigb:expr) => { + $crate::perun_types::ChannelWitnessUnion::Close( + $crate::perun_types::Close::new_builder() + .state($state) + .sig_a($siga) + .sig_b($sigb) + .build(), + ) + }; +} + +#[macro_export] +macro_rules! dispute { + ($siga:expr, $sigb:expr) => { + $crate::perun_types::ChannelWitnessUnion::Dispute( + $crate::perun_types::Dispute::new_builder() + .sig_a($siga) + .sig_b($sigb) + .build(), + ) + }; +} + +impl SUDTDistribution { + pub fn sum(&self) -> u128 { + let a: u128 = self.nth0().unpack(); + let b: u128 = self.nth1().unpack(); + a + b + } + + pub fn equal(&self, other: &Balances) -> bool { + self.as_slice()[..] == other.as_slice()[..] + } + + pub fn get(&self, i: usize) -> Result { + match i { + 0 => Ok(self.nth0().unpack()), + 1 => Ok(self.nth1().unpack()), + _ => Err(Error::IndexOutOfBound), + } + } + + pub fn clear_index(&self, idx: usize) -> Result { + match idx { + 0 => Ok(self.clone().as_builder().nth0(0u128.pack()).build()), + 1 => Ok(self.clone().as_builder().nth1(0u128.pack()).build()), + _ => Err(Error::IndexOutOfBound), + } + } + + pub fn from_array(a: [u128; 2]) -> Self { + SUDTDistribution::new_builder() + .nth0(a[0].pack()) + .nth1(a[1].pack()) + .build() + } + + pub fn to_array(&self) -> [u128; 2] { + [self.nth0().unpack(), self.nth1().unpack()] + } +} + +impl Balances { + pub fn clear_index(&self, idx: usize) -> Result { + let ckbytes = self.ckbytes().clear_index(idx)?; + let mut sudts: Vec = Vec::new(); + for sb in self.sudts().into_iter() { + sudts.push( + sb.clone() + .as_builder() + .distribution(sb.distribution().clear_index(idx)?) + .build(), + ); + } + Ok(self + .clone() + .as_builder() + .ckbytes(ckbytes) + .sudts(SUDTAllocation::new_builder().set(sudts).build()) + .build()) + } + + pub fn zero_at_index(&self, idx: usize) -> Result { + if self.ckbytes().get(idx)? != 0u64 { + return Ok(false); + } + for sb in self.sudts().into_iter() { + if sb.distribution().get(idx)? != 0u128 { + return Ok(false); + } + } + return Ok(true); + } + + pub fn equal_at_index(&self, other: &Balances, idx: usize) -> Result { + if self.ckbytes().get(idx)? != other.ckbytes().get(idx)? { + return Ok(false); + } + if self.sudts().len() != other.sudts().len() { + return Ok(false); + } + for (i, sb) in self.sudts().into_iter().enumerate() { + let other_sb = other.sudts().get(i).ok_or(Error::IndexOutOfBound)?; + if sb.asset().as_slice() != other_sb.as_slice() { + return Ok(false); + } + if sb.distribution().get(idx)? != other_sb.distribution().get(idx)? { + return Ok(false); + } + } + return Ok(true); + } + + pub fn equal_in_sum(&self, other: &Balances) -> Result { + if self.ckbytes().sum() != other.ckbytes().sum() { + return Ok(false); + } + if self.sudts().len() != other.sudts().len() { + return Ok(false); + } + for (i, sb) in self.sudts().into_iter().enumerate() { + let other_sb = other.sudts().get(i).ok_or(Error::IndexOutOfBound)?; + if sb.asset().as_slice() != other_sb.asset().as_slice() { + return Ok(false); + } + if sb.distribution().sum() != other_sb.distribution().sum() { + return Ok(false); + } + } + return Ok(true); + } + + pub fn equal(&self, other: &Balances) -> bool { + self.as_slice()[..] == other.as_slice()[..] + } +} + +impl SUDTAllocation { + pub fn get_locked_ckbytes(&self) -> u64 { + let mut sum: u64 = 0u64; + for sudt in self.clone().into_iter() { + let min_cap: u64 = sudt.asset().max_capacity().unpack(); + sum += min_cap; + } + return sum; + } + + pub fn get_distribution(&self, sudt: &Script) -> Result<(usize, SUDTDistribution), Error> { + for (i, sb) in self.clone().into_iter().enumerate() { + if sb.asset().type_script().as_slice() == sudt.as_slice() { + return Ok((i, sb.distribution())); + } + } + return Err(Error::InvalidSUDT); + } + + pub fn fully_represented(&self, idx: usize, values: &[u128]) -> Result { + if values.len() < self.len() { + return Ok(false); + } + for (i, sb) in self.clone().into_iter().enumerate() { + let v = sb.distribution().get(idx)?; + if values[i] < v { + return Ok(false); + } + } + return Ok(true); + } +} + +impl CKByteDistribution { + pub fn sum(&self) -> u64 { + let a: u64 = self.nth0().unpack(); + let b: u64 = self.nth1().unpack(); + a + b + } + + pub fn equal(&self, other: &Balances) -> bool { + self.as_slice()[..] == other.as_slice()[..] + } + + pub fn get(&self, i: usize) -> Result { + match i { + 0 => Ok(self.nth0().unpack()), + 1 => Ok(self.nth1().unpack()), + _ => Err(Error::IndexOutOfBound), + } + } + + pub fn clear_index(&self, idx: usize) -> Result { + match idx { + 0 => Ok(self.clone().as_builder().nth0(0u64.pack()).build()), + 1 => Ok(self.clone().as_builder().nth1(0u64.pack()).build()), + _ => Err(Error::IndexOutOfBound), + } + } + + pub fn from_array(array: [u64; 2]) -> Self { + CKByteDistribution::new_builder() + .nth0(array[0].pack()) + .nth1(array[1].pack()) + .build() + } + + pub fn to_array(&self) -> [u64; 2] { + [self.nth0().unpack(), self.nth1().unpack()] + } +} + +pub fn geq_components(fst: &CKByteDistribution, snd: &CKByteDistribution) -> bool { + let a_fst: u64 = fst.nth0().unpack(); + let a_snd: u64 = snd.nth0().unpack(); + let b_fst: u64 = fst.nth1().unpack(); + let b_snd: u64 = snd.nth1().unpack(); + a_fst >= a_snd && b_fst >= b_snd +} + +pub const CKB_HASH_PERSONALIZATION: &[u8] = b"ckb-default-hash"; + +pub fn blake2b256(data: &[u8]) -> [u8; 32] { + let mut result = [0u8; 32]; + let mut blake2b = Blake2bBuilder::new(32) + //.personal(CKB_HASH_PERSONALIZATION) + .build(); + blake2b.update(data); + blake2b.finalize(&mut result); + result +} + +impl ChannelStatus { + // mk_funded creates a new ChannelStatus with the funded flag set to true. + pub fn mk_funded(self) -> ChannelStatus { + self.clone().as_builder().funded(ctrue!()).build() + } + + #[cfg(feature = "std")] + /// mk_close_outputs creates the outputs for a close transaction according to the current + /// channel state. It does not matter whether the ChannelState in question is finalized or not. + pub fn mk_close_outputs( + self, + mk_lock_script: impl FnMut(u8) -> Script, + ) -> Vec<(CellOutput, bytes::Bytes)> { + self.state().mk_outputs(mk_lock_script) + } +} + +#[cfg(feature = "std")] +impl ChannelState { + pub fn mk_outputs( + self, + mk_lock_script: impl FnMut(u8) -> Script, + ) -> Vec<(CellOutput, bytes::Bytes)> { + return self.balances().mk_outputs(mk_lock_script, vec![0, 1]); + } +} + +#[cfg(feature = "std")] +impl Balances { + pub fn mk_outputs( + self, + mut mk_lock_script: impl FnMut(u8) -> Script, + indices: Vec, + ) -> Vec<(CellOutput, bytes::Bytes)> { + let mut ckbytes = self + .ckbytes() + .mk_outputs(&mut mk_lock_script, indices.clone()); + let mut sudts = self.sudts().mk_outputs(mk_lock_script, indices); + ckbytes.append(&mut sudts); + return ckbytes; + } +} + +#[cfg(feature = "std")] +impl CKByteDistribution { + pub fn mk_outputs( + self, + mut mk_lock_script: impl FnMut(u8) -> Script, + indices: Vec, + ) -> Vec<(CellOutput, bytes::Bytes)> { + // TODO: Outputs should contain min-capacity for script size... + indices + .iter() + .fold(vec![], |mut acc: Vec<(CellOutput, bytes::Bytes)>, index| { + let cap = self.get(index.clone() as usize).expect("invalid index"); + acc.push(( + CellOutput::new_builder() + .capacity(cap.pack()) + .lock(mk_lock_script(*index)) + .build(), + bytes::Bytes::new(), + )); + acc + }) + } +} + +#[cfg(feature = "std")] +impl SUDTAllocation { + pub fn mk_outputs( + self, + mut mk_lock_script: impl FnMut(u8) -> Script, + indices: Vec, + ) -> Vec<(CellOutput, bytes::Bytes)> { + let mut outputs: Vec<(CellOutput, bytes::Bytes)> = Vec::new(); + for (i, balance) in self.into_iter().enumerate() { + let udt_type = balance.asset().type_script(); + let udt_type_opt = ScriptOpt::new_builder().set(Some(udt_type)).build(); + let cap: u64 = balance.asset().max_capacity().unpack(); + for f in indices.iter() { + if balance + .distribution() + .get(*f as usize) + .expect("invalid index") + == 0u128 + { + outputs.push(( + CellOutput::new_builder() + .capacity(cap.pack()) + .lock(mk_lock_script(*f)) + .build(), + bytes::Bytes::new(), + )); + } else { + outputs.push(( + CellOutput::new_builder() + .capacity(cap.pack()) + .lock(mk_lock_script(*f)) + .type_(udt_type_opt.clone()) + .build(), + bytes::Bytes::from( + balance + .distribution() + .get(*f as usize) + .expect("invalid index") + .to_le_bytes() + .to_vec(), + ), + )); + } + } + } + return outputs; + } +} + +impl ChannelParameters { + /// mk_party_pubkeys creates a vector of each participants public key in the correct order. + pub fn mk_party_pubkeys(self) -> Vec> { + vec![ + self.party_a().pub_key().to_vec(), + self.party_b().pub_key().to_vec(), + ] + } +} + +impl SEC1EncodedPubKey { + pub fn to_vec(&self) -> Vec { + self.as_bytes().to_vec() + } +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/lib.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/lib.rs new file mode 100644 index 0000000..39d0017 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod error; +pub mod helpers; +#[allow(clippy::all)] +pub mod perun_types; +pub mod sig; diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/perun_types.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/perun_types.rs new file mode 100644 index 0000000..61fe220 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/perun_types.rs @@ -0,0 +1,6561 @@ +// Generated by Molecule 0.7.3 +#![allow(unused_imports)] + +#[cfg(feature = "std")] +use {ckb_types::packed::*, ckb_types::prelude::*}; + +#[cfg(not(feature = "std"))] +use {ckb_standalone_types::packed::*, ckb_standalone_types::prelude::*}; + +use molecule::prelude::*; + +#[derive(Clone)] +pub struct SEC1EncodedPubKey(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for SEC1EncodedPubKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for SEC1EncodedPubKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for SEC1EncodedPubKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for SEC1EncodedPubKey { + fn default() -> Self { + let v: Vec = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ]; + SEC1EncodedPubKey::new_unchecked(v.into()) + } +} +impl SEC1EncodedPubKey { + pub const TOTAL_SIZE: usize = 33; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 33; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn nth1(&self) -> Byte { + Byte::new_unchecked(self.0.slice(1..2)) + } + pub fn nth2(&self) -> Byte { + Byte::new_unchecked(self.0.slice(2..3)) + } + pub fn nth3(&self) -> Byte { + Byte::new_unchecked(self.0.slice(3..4)) + } + pub fn nth4(&self) -> Byte { + Byte::new_unchecked(self.0.slice(4..5)) + } + pub fn nth5(&self) -> Byte { + Byte::new_unchecked(self.0.slice(5..6)) + } + pub fn nth6(&self) -> Byte { + Byte::new_unchecked(self.0.slice(6..7)) + } + pub fn nth7(&self) -> Byte { + Byte::new_unchecked(self.0.slice(7..8)) + } + pub fn nth8(&self) -> Byte { + Byte::new_unchecked(self.0.slice(8..9)) + } + pub fn nth9(&self) -> Byte { + Byte::new_unchecked(self.0.slice(9..10)) + } + pub fn nth10(&self) -> Byte { + Byte::new_unchecked(self.0.slice(10..11)) + } + pub fn nth11(&self) -> Byte { + Byte::new_unchecked(self.0.slice(11..12)) + } + pub fn nth12(&self) -> Byte { + Byte::new_unchecked(self.0.slice(12..13)) + } + pub fn nth13(&self) -> Byte { + Byte::new_unchecked(self.0.slice(13..14)) + } + pub fn nth14(&self) -> Byte { + Byte::new_unchecked(self.0.slice(14..15)) + } + pub fn nth15(&self) -> Byte { + Byte::new_unchecked(self.0.slice(15..16)) + } + pub fn nth16(&self) -> Byte { + Byte::new_unchecked(self.0.slice(16..17)) + } + pub fn nth17(&self) -> Byte { + Byte::new_unchecked(self.0.slice(17..18)) + } + pub fn nth18(&self) -> Byte { + Byte::new_unchecked(self.0.slice(18..19)) + } + pub fn nth19(&self) -> Byte { + Byte::new_unchecked(self.0.slice(19..20)) + } + pub fn nth20(&self) -> Byte { + Byte::new_unchecked(self.0.slice(20..21)) + } + pub fn nth21(&self) -> Byte { + Byte::new_unchecked(self.0.slice(21..22)) + } + pub fn nth22(&self) -> Byte { + Byte::new_unchecked(self.0.slice(22..23)) + } + pub fn nth23(&self) -> Byte { + Byte::new_unchecked(self.0.slice(23..24)) + } + pub fn nth24(&self) -> Byte { + Byte::new_unchecked(self.0.slice(24..25)) + } + pub fn nth25(&self) -> Byte { + Byte::new_unchecked(self.0.slice(25..26)) + } + pub fn nth26(&self) -> Byte { + Byte::new_unchecked(self.0.slice(26..27)) + } + pub fn nth27(&self) -> Byte { + Byte::new_unchecked(self.0.slice(27..28)) + } + pub fn nth28(&self) -> Byte { + Byte::new_unchecked(self.0.slice(28..29)) + } + pub fn nth29(&self) -> Byte { + Byte::new_unchecked(self.0.slice(29..30)) + } + pub fn nth30(&self) -> Byte { + Byte::new_unchecked(self.0.slice(30..31)) + } + pub fn nth31(&self) -> Byte { + Byte::new_unchecked(self.0.slice(31..32)) + } + pub fn nth32(&self) -> Byte { + Byte::new_unchecked(self.0.slice(32..33)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> SEC1EncodedPubKeyReader<'r> { + SEC1EncodedPubKeyReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for SEC1EncodedPubKey { + type Builder = SEC1EncodedPubKeyBuilder; + const NAME: &'static str = "SEC1EncodedPubKey"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + SEC1EncodedPubKey(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SEC1EncodedPubKeyReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SEC1EncodedPubKeyReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([ + self.nth0(), + self.nth1(), + self.nth2(), + self.nth3(), + self.nth4(), + self.nth5(), + self.nth6(), + self.nth7(), + self.nth8(), + self.nth9(), + self.nth10(), + self.nth11(), + self.nth12(), + self.nth13(), + self.nth14(), + self.nth15(), + self.nth16(), + self.nth17(), + self.nth18(), + self.nth19(), + self.nth20(), + self.nth21(), + self.nth22(), + self.nth23(), + self.nth24(), + self.nth25(), + self.nth26(), + self.nth27(), + self.nth28(), + self.nth29(), + self.nth30(), + self.nth31(), + self.nth32(), + ]) + } +} +#[derive(Clone, Copy)] +pub struct SEC1EncodedPubKeyReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for SEC1EncodedPubKeyReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for SEC1EncodedPubKeyReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for SEC1EncodedPubKeyReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> SEC1EncodedPubKeyReader<'r> { + pub const TOTAL_SIZE: usize = 33; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 33; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn nth1(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[1..2]) + } + pub fn nth2(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[2..3]) + } + pub fn nth3(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[3..4]) + } + pub fn nth4(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[4..5]) + } + pub fn nth5(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[5..6]) + } + pub fn nth6(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[6..7]) + } + pub fn nth7(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[7..8]) + } + pub fn nth8(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[8..9]) + } + pub fn nth9(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[9..10]) + } + pub fn nth10(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[10..11]) + } + pub fn nth11(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[11..12]) + } + pub fn nth12(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[12..13]) + } + pub fn nth13(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[13..14]) + } + pub fn nth14(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[14..15]) + } + pub fn nth15(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[15..16]) + } + pub fn nth16(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[16..17]) + } + pub fn nth17(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[17..18]) + } + pub fn nth18(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[18..19]) + } + pub fn nth19(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[19..20]) + } + pub fn nth20(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[20..21]) + } + pub fn nth21(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[21..22]) + } + pub fn nth22(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[22..23]) + } + pub fn nth23(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[23..24]) + } + pub fn nth24(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[24..25]) + } + pub fn nth25(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[25..26]) + } + pub fn nth26(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[26..27]) + } + pub fn nth27(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[27..28]) + } + pub fn nth28(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[28..29]) + } + pub fn nth29(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[29..30]) + } + pub fn nth30(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[30..31]) + } + pub fn nth31(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[31..32]) + } + pub fn nth32(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[32..33]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for SEC1EncodedPubKeyReader<'r> { + type Entity = SEC1EncodedPubKey; + const NAME: &'static str = "SEC1EncodedPubKeyReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + SEC1EncodedPubKeyReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct SEC1EncodedPubKeyBuilder(pub(crate) [Byte; 33]); +impl ::core::fmt::Debug for SEC1EncodedPubKeyBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for SEC1EncodedPubKeyBuilder { + fn default() -> Self { + SEC1EncodedPubKeyBuilder([ + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + Byte::default(), + ]) + } +} +impl SEC1EncodedPubKeyBuilder { + pub const TOTAL_SIZE: usize = 33; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 33; + pub fn set(mut self, v: [Byte; 33]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } + pub fn nth1(mut self, v: Byte) -> Self { + self.0[1] = v; + self + } + pub fn nth2(mut self, v: Byte) -> Self { + self.0[2] = v; + self + } + pub fn nth3(mut self, v: Byte) -> Self { + self.0[3] = v; + self + } + pub fn nth4(mut self, v: Byte) -> Self { + self.0[4] = v; + self + } + pub fn nth5(mut self, v: Byte) -> Self { + self.0[5] = v; + self + } + pub fn nth6(mut self, v: Byte) -> Self { + self.0[6] = v; + self + } + pub fn nth7(mut self, v: Byte) -> Self { + self.0[7] = v; + self + } + pub fn nth8(mut self, v: Byte) -> Self { + self.0[8] = v; + self + } + pub fn nth9(mut self, v: Byte) -> Self { + self.0[9] = v; + self + } + pub fn nth10(mut self, v: Byte) -> Self { + self.0[10] = v; + self + } + pub fn nth11(mut self, v: Byte) -> Self { + self.0[11] = v; + self + } + pub fn nth12(mut self, v: Byte) -> Self { + self.0[12] = v; + self + } + pub fn nth13(mut self, v: Byte) -> Self { + self.0[13] = v; + self + } + pub fn nth14(mut self, v: Byte) -> Self { + self.0[14] = v; + self + } + pub fn nth15(mut self, v: Byte) -> Self { + self.0[15] = v; + self + } + pub fn nth16(mut self, v: Byte) -> Self { + self.0[16] = v; + self + } + pub fn nth17(mut self, v: Byte) -> Self { + self.0[17] = v; + self + } + pub fn nth18(mut self, v: Byte) -> Self { + self.0[18] = v; + self + } + pub fn nth19(mut self, v: Byte) -> Self { + self.0[19] = v; + self + } + pub fn nth20(mut self, v: Byte) -> Self { + self.0[20] = v; + self + } + pub fn nth21(mut self, v: Byte) -> Self { + self.0[21] = v; + self + } + pub fn nth22(mut self, v: Byte) -> Self { + self.0[22] = v; + self + } + pub fn nth23(mut self, v: Byte) -> Self { + self.0[23] = v; + self + } + pub fn nth24(mut self, v: Byte) -> Self { + self.0[24] = v; + self + } + pub fn nth25(mut self, v: Byte) -> Self { + self.0[25] = v; + self + } + pub fn nth26(mut self, v: Byte) -> Self { + self.0[26] = v; + self + } + pub fn nth27(mut self, v: Byte) -> Self { + self.0[27] = v; + self + } + pub fn nth28(mut self, v: Byte) -> Self { + self.0[28] = v; + self + } + pub fn nth29(mut self, v: Byte) -> Self { + self.0[29] = v; + self + } + pub fn nth30(mut self, v: Byte) -> Self { + self.0[30] = v; + self + } + pub fn nth31(mut self, v: Byte) -> Self { + self.0[31] = v; + self + } + pub fn nth32(mut self, v: Byte) -> Self { + self.0[32] = v; + self + } +} +impl molecule::prelude::Builder for SEC1EncodedPubKeyBuilder { + type Entity = SEC1EncodedPubKey; + const NAME: &'static str = "SEC1EncodedPubKeyBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + writer.write_all(self.0[1].as_slice())?; + writer.write_all(self.0[2].as_slice())?; + writer.write_all(self.0[3].as_slice())?; + writer.write_all(self.0[4].as_slice())?; + writer.write_all(self.0[5].as_slice())?; + writer.write_all(self.0[6].as_slice())?; + writer.write_all(self.0[7].as_slice())?; + writer.write_all(self.0[8].as_slice())?; + writer.write_all(self.0[9].as_slice())?; + writer.write_all(self.0[10].as_slice())?; + writer.write_all(self.0[11].as_slice())?; + writer.write_all(self.0[12].as_slice())?; + writer.write_all(self.0[13].as_slice())?; + writer.write_all(self.0[14].as_slice())?; + writer.write_all(self.0[15].as_slice())?; + writer.write_all(self.0[16].as_slice())?; + writer.write_all(self.0[17].as_slice())?; + writer.write_all(self.0[18].as_slice())?; + writer.write_all(self.0[19].as_slice())?; + writer.write_all(self.0[20].as_slice())?; + writer.write_all(self.0[21].as_slice())?; + writer.write_all(self.0[22].as_slice())?; + writer.write_all(self.0[23].as_slice())?; + writer.write_all(self.0[24].as_slice())?; + writer.write_all(self.0[25].as_slice())?; + writer.write_all(self.0[26].as_slice())?; + writer.write_all(self.0[27].as_slice())?; + writer.write_all(self.0[28].as_slice())?; + writer.write_all(self.0[29].as_slice())?; + writer.write_all(self.0[30].as_slice())?; + writer.write_all(self.0[31].as_slice())?; + writer.write_all(self.0[32].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + SEC1EncodedPubKey::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct CKByteDistribution(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for CKByteDistribution { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for CKByteDistribution { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for CKByteDistribution { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + write!(f, "{}", self.nth0())?; + write!(f, ", {}", self.nth1())?; + write!(f, "]") + } +} +impl ::core::default::Default for CKByteDistribution { + fn default() -> Self { + let v: Vec = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + CKByteDistribution::new_unchecked(v.into()) + } +} +impl CKByteDistribution { + pub const TOTAL_SIZE: usize = 16; + pub const ITEM_SIZE: usize = 8; + pub const ITEM_COUNT: usize = 2; + pub fn nth0(&self) -> Uint64 { + Uint64::new_unchecked(self.0.slice(0..8)) + } + pub fn nth1(&self) -> Uint64 { + Uint64::new_unchecked(self.0.slice(8..16)) + } + pub fn as_reader<'r>(&'r self) -> CKByteDistributionReader<'r> { + CKByteDistributionReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for CKByteDistribution { + type Builder = CKByteDistributionBuilder; + const NAME: &'static str = "CKByteDistribution"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + CKByteDistribution(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + CKByteDistributionReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + CKByteDistributionReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0(), self.nth1()]) + } +} +#[derive(Clone, Copy)] +pub struct CKByteDistributionReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for CKByteDistributionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for CKByteDistributionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for CKByteDistributionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + write!(f, "{}", self.nth0())?; + write!(f, ", {}", self.nth1())?; + write!(f, "]") + } +} +impl<'r> CKByteDistributionReader<'r> { + pub const TOTAL_SIZE: usize = 16; + pub const ITEM_SIZE: usize = 8; + pub const ITEM_COUNT: usize = 2; + pub fn nth0(&self) -> Uint64Reader<'r> { + Uint64Reader::new_unchecked(&self.as_slice()[0..8]) + } + pub fn nth1(&self) -> Uint64Reader<'r> { + Uint64Reader::new_unchecked(&self.as_slice()[8..16]) + } +} +impl<'r> molecule::prelude::Reader<'r> for CKByteDistributionReader<'r> { + type Entity = CKByteDistribution; + const NAME: &'static str = "CKByteDistributionReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + CKByteDistributionReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct CKByteDistributionBuilder(pub(crate) [Uint64; 2]); +impl ::core::fmt::Debug for CKByteDistributionBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for CKByteDistributionBuilder { + fn default() -> Self { + CKByteDistributionBuilder([Uint64::default(), Uint64::default()]) + } +} +impl CKByteDistributionBuilder { + pub const TOTAL_SIZE: usize = 16; + pub const ITEM_SIZE: usize = 8; + pub const ITEM_COUNT: usize = 2; + pub fn set(mut self, v: [Uint64; 2]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Uint64) -> Self { + self.0[0] = v; + self + } + pub fn nth1(mut self, v: Uint64) -> Self { + self.0[1] = v; + self + } +} +impl molecule::prelude::Builder for CKByteDistributionBuilder { + type Entity = CKByteDistribution; + const NAME: &'static str = "CKByteDistributionBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + writer.write_all(self.0[1].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + CKByteDistribution::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct SUDTDistribution(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for SUDTDistribution { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for SUDTDistribution { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for SUDTDistribution { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + write!(f, "{}", self.nth0())?; + write!(f, ", {}", self.nth1())?; + write!(f, "]") + } +} +impl ::core::default::Default for SUDTDistribution { + fn default() -> Self { + let v: Vec = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]; + SUDTDistribution::new_unchecked(v.into()) + } +} +impl SUDTDistribution { + pub const TOTAL_SIZE: usize = 32; + pub const ITEM_SIZE: usize = 16; + pub const ITEM_COUNT: usize = 2; + pub fn nth0(&self) -> Uint128 { + Uint128::new_unchecked(self.0.slice(0..16)) + } + pub fn nth1(&self) -> Uint128 { + Uint128::new_unchecked(self.0.slice(16..32)) + } + pub fn as_reader<'r>(&'r self) -> SUDTDistributionReader<'r> { + SUDTDistributionReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for SUDTDistribution { + type Builder = SUDTDistributionBuilder; + const NAME: &'static str = "SUDTDistribution"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + SUDTDistribution(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTDistributionReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTDistributionReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0(), self.nth1()]) + } +} +#[derive(Clone, Copy)] +pub struct SUDTDistributionReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for SUDTDistributionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for SUDTDistributionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for SUDTDistributionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + write!(f, "{}", self.nth0())?; + write!(f, ", {}", self.nth1())?; + write!(f, "]") + } +} +impl<'r> SUDTDistributionReader<'r> { + pub const TOTAL_SIZE: usize = 32; + pub const ITEM_SIZE: usize = 16; + pub const ITEM_COUNT: usize = 2; + pub fn nth0(&self) -> Uint128Reader<'r> { + Uint128Reader::new_unchecked(&self.as_slice()[0..16]) + } + pub fn nth1(&self) -> Uint128Reader<'r> { + Uint128Reader::new_unchecked(&self.as_slice()[16..32]) + } +} +impl<'r> molecule::prelude::Reader<'r> for SUDTDistributionReader<'r> { + type Entity = SUDTDistribution; + const NAME: &'static str = "SUDTDistributionReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + SUDTDistributionReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct SUDTDistributionBuilder(pub(crate) [Uint128; 2]); +impl ::core::fmt::Debug for SUDTDistributionBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for SUDTDistributionBuilder { + fn default() -> Self { + SUDTDistributionBuilder([Uint128::default(), Uint128::default()]) + } +} +impl SUDTDistributionBuilder { + pub const TOTAL_SIZE: usize = 32; + pub const ITEM_SIZE: usize = 16; + pub const ITEM_COUNT: usize = 2; + pub fn set(mut self, v: [Uint128; 2]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Uint128) -> Self { + self.0[0] = v; + self + } + pub fn nth1(mut self, v: Uint128) -> Self { + self.0[1] = v; + self + } +} +impl molecule::prelude::Builder for SUDTDistributionBuilder { + type Entity = SUDTDistribution; + const NAME: &'static str = "SUDTDistributionBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + writer.write_all(self.0[1].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + SUDTDistribution::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct SUDTAllocation(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for SUDTAllocation { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for SUDTAllocation { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for SUDTAllocation { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + for i in 0..self.len() { + if i == 0 { + write!(f, "{}", self.get_unchecked(i))?; + } else { + write!(f, ", {}", self.get_unchecked(i))?; + } + } + write!(f, "]") + } +} +impl ::core::default::Default for SUDTAllocation { + fn default() -> Self { + let v: Vec = vec![4, 0, 0, 0]; + SUDTAllocation::new_unchecked(v.into()) + } +} +impl SUDTAllocation { + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn item_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn len(&self) -> usize { + self.item_count() + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn get(&self, idx: usize) -> Option { + if idx >= self.len() { + None + } else { + Some(self.get_unchecked(idx)) + } + } + pub fn get_unchecked(&self, idx: usize) -> SUDTBalances { + let slice = self.as_slice(); + let start_idx = molecule::NUMBER_SIZE * (1 + idx); + let start = molecule::unpack_number(&slice[start_idx..]) as usize; + if idx == self.len() - 1 { + SUDTBalances::new_unchecked(self.0.slice(start..)) + } else { + let end_idx = start_idx + molecule::NUMBER_SIZE; + let end = molecule::unpack_number(&slice[end_idx..]) as usize; + SUDTBalances::new_unchecked(self.0.slice(start..end)) + } + } + pub fn as_reader<'r>(&'r self) -> SUDTAllocationReader<'r> { + SUDTAllocationReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for SUDTAllocation { + type Builder = SUDTAllocationBuilder; + const NAME: &'static str = "SUDTAllocation"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + SUDTAllocation(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTAllocationReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTAllocationReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().extend(self.into_iter()) + } +} +#[derive(Clone, Copy)] +pub struct SUDTAllocationReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for SUDTAllocationReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for SUDTAllocationReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for SUDTAllocationReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + for i in 0..self.len() { + if i == 0 { + write!(f, "{}", self.get_unchecked(i))?; + } else { + write!(f, ", {}", self.get_unchecked(i))?; + } + } + write!(f, "]") + } +} +impl<'r> SUDTAllocationReader<'r> { + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn item_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn len(&self) -> usize { + self.item_count() + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn get(&self, idx: usize) -> Option> { + if idx >= self.len() { + None + } else { + Some(self.get_unchecked(idx)) + } + } + pub fn get_unchecked(&self, idx: usize) -> SUDTBalancesReader<'r> { + let slice = self.as_slice(); + let start_idx = molecule::NUMBER_SIZE * (1 + idx); + let start = molecule::unpack_number(&slice[start_idx..]) as usize; + if idx == self.len() - 1 { + SUDTBalancesReader::new_unchecked(&self.as_slice()[start..]) + } else { + let end_idx = start_idx + molecule::NUMBER_SIZE; + let end = molecule::unpack_number(&slice[end_idx..]) as usize; + SUDTBalancesReader::new_unchecked(&self.as_slice()[start..end]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for SUDTAllocationReader<'r> { + type Entity = SUDTAllocation; + const NAME: &'static str = "SUDTAllocationReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + SUDTAllocationReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!( + Self, + TotalSizeNotMatch, + molecule::NUMBER_SIZE * 2, + slice_len + ); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + for pair in offsets.windows(2) { + let start = pair[0]; + let end = pair[1]; + SUDTBalancesReader::verify(&slice[start..end], compatible)?; + } + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct SUDTAllocationBuilder(pub(crate) Vec); +impl SUDTAllocationBuilder { + pub fn set(mut self, v: Vec) -> Self { + self.0 = v; + self + } + pub fn push(mut self, v: SUDTBalances) -> Self { + self.0.push(v); + self + } + pub fn extend>(mut self, iter: T) -> Self { + for elem in iter { + self.0.push(elem); + } + self + } + pub fn replace(&mut self, index: usize, v: SUDTBalances) -> Option { + self.0 + .get_mut(index) + .map(|item| ::core::mem::replace(item, v)) + } +} +impl molecule::prelude::Builder for SUDTAllocationBuilder { + type Entity = SUDTAllocation; + const NAME: &'static str = "SUDTAllocationBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (self.0.len() + 1) + + self + .0 + .iter() + .map(|inner| inner.as_slice().len()) + .sum::() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let item_count = self.0.len(); + if item_count == 0 { + writer.write_all(&molecule::pack_number( + molecule::NUMBER_SIZE as molecule::Number, + ))?; + } else { + let (total_size, offsets) = self.0.iter().fold( + ( + molecule::NUMBER_SIZE * (item_count + 1), + Vec::with_capacity(item_count), + ), + |(start, mut offsets), inner| { + offsets.push(start); + (start + inner.as_slice().len(), offsets) + }, + ); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + for inner in self.0.iter() { + writer.write_all(inner.as_slice())?; + } + } + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + SUDTAllocation::new_unchecked(inner.into()) + } +} +pub struct SUDTAllocationIterator(SUDTAllocation, usize, usize); +impl ::core::iter::Iterator for SUDTAllocationIterator { + type Item = SUDTBalances; + fn next(&mut self) -> Option { + if self.1 >= self.2 { + None + } else { + let ret = self.0.get_unchecked(self.1); + self.1 += 1; + Some(ret) + } + } +} +impl ::core::iter::ExactSizeIterator for SUDTAllocationIterator { + fn len(&self) -> usize { + self.2 - self.1 + } +} +impl ::core::iter::IntoIterator for SUDTAllocation { + type Item = SUDTBalances; + type IntoIter = SUDTAllocationIterator; + fn into_iter(self) -> Self::IntoIter { + let len = self.len(); + SUDTAllocationIterator(self, 0, len) + } +} +impl<'r> SUDTAllocationReader<'r> { + pub fn iter<'t>(&'t self) -> SUDTAllocationReaderIterator<'t, 'r> { + SUDTAllocationReaderIterator(&self, 0, self.len()) + } +} +pub struct SUDTAllocationReaderIterator<'t, 'r>(&'t SUDTAllocationReader<'r>, usize, usize); +impl<'t: 'r, 'r> ::core::iter::Iterator for SUDTAllocationReaderIterator<'t, 'r> { + type Item = SUDTBalancesReader<'t>; + fn next(&mut self) -> Option { + if self.1 >= self.2 { + None + } else { + let ret = self.0.get_unchecked(self.1); + self.1 += 1; + Some(ret) + } + } +} +impl<'t: 'r, 'r> ::core::iter::ExactSizeIterator for SUDTAllocationReaderIterator<'t, 'r> { + fn len(&self) -> usize { + self.2 - self.1 + } +} +#[derive(Clone)] +pub struct SUDTAsset(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for SUDTAsset { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for SUDTAsset { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for SUDTAsset { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "type_script", self.type_script())?; + write!(f, ", {}: {}", "max_capacity", self.max_capacity())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for SUDTAsset { + fn default() -> Self { + let v: Vec = vec![ + 73, 0, 0, 0, 12, 0, 0, 0, 65, 0, 0, 0, 53, 0, 0, 0, 16, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + SUDTAsset::new_unchecked(v.into()) + } +} +impl SUDTAsset { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn type_script(&self) -> Script { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Script::new_unchecked(self.0.slice(start..end)) + } + pub fn max_capacity(&self) -> Uint64 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + Uint64::new_unchecked(self.0.slice(start..end)) + } else { + Uint64::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> SUDTAssetReader<'r> { + SUDTAssetReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for SUDTAsset { + type Builder = SUDTAssetBuilder; + const NAME: &'static str = "SUDTAsset"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + SUDTAsset(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTAssetReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTAssetReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .type_script(self.type_script()) + .max_capacity(self.max_capacity()) + } +} +#[derive(Clone, Copy)] +pub struct SUDTAssetReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for SUDTAssetReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for SUDTAssetReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for SUDTAssetReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "type_script", self.type_script())?; + write!(f, ", {}: {}", "max_capacity", self.max_capacity())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> SUDTAssetReader<'r> { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn type_script(&self) -> ScriptReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ScriptReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn max_capacity(&self) -> Uint64Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + Uint64Reader::new_unchecked(&self.as_slice()[start..end]) + } else { + Uint64Reader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for SUDTAssetReader<'r> { + type Entity = SUDTAsset; + const NAME: &'static str = "SUDTAssetReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + SUDTAssetReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + ScriptReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + Uint64Reader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct SUDTAssetBuilder { + pub(crate) type_script: Script, + pub(crate) max_capacity: Uint64, +} +impl SUDTAssetBuilder { + pub const FIELD_COUNT: usize = 2; + pub fn type_script(mut self, v: Script) -> Self { + self.type_script = v; + self + } + pub fn max_capacity(mut self, v: Uint64) -> Self { + self.max_capacity = v; + self + } +} +impl molecule::prelude::Builder for SUDTAssetBuilder { + type Entity = SUDTAsset; + const NAME: &'static str = "SUDTAssetBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.type_script.as_slice().len() + + self.max_capacity.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.type_script.as_slice().len(); + offsets.push(total_size); + total_size += self.max_capacity.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.type_script.as_slice())?; + writer.write_all(self.max_capacity.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + SUDTAsset::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct SUDTBalances(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for SUDTBalances { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for SUDTBalances { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for SUDTBalances { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "asset", self.asset())?; + write!(f, ", {}: {}", "distribution", self.distribution())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for SUDTBalances { + fn default() -> Self { + let v: Vec = vec![ + 117, 0, 0, 0, 12, 0, 0, 0, 85, 0, 0, 0, 73, 0, 0, 0, 12, 0, 0, 0, 65, 0, 0, 0, 53, 0, + 0, 0, 16, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + ]; + SUDTBalances::new_unchecked(v.into()) + } +} +impl SUDTBalances { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn asset(&self) -> SUDTAsset { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + SUDTAsset::new_unchecked(self.0.slice(start..end)) + } + pub fn distribution(&self) -> SUDTDistribution { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + SUDTDistribution::new_unchecked(self.0.slice(start..end)) + } else { + SUDTDistribution::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> SUDTBalancesReader<'r> { + SUDTBalancesReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for SUDTBalances { + type Builder = SUDTBalancesBuilder; + const NAME: &'static str = "SUDTBalances"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + SUDTBalances(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTBalancesReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SUDTBalancesReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .asset(self.asset()) + .distribution(self.distribution()) + } +} +#[derive(Clone, Copy)] +pub struct SUDTBalancesReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for SUDTBalancesReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for SUDTBalancesReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for SUDTBalancesReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "asset", self.asset())?; + write!(f, ", {}: {}", "distribution", self.distribution())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> SUDTBalancesReader<'r> { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn asset(&self) -> SUDTAssetReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + SUDTAssetReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn distribution(&self) -> SUDTDistributionReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + SUDTDistributionReader::new_unchecked(&self.as_slice()[start..end]) + } else { + SUDTDistributionReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for SUDTBalancesReader<'r> { + type Entity = SUDTBalances; + const NAME: &'static str = "SUDTBalancesReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + SUDTBalancesReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + SUDTAssetReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + SUDTDistributionReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct SUDTBalancesBuilder { + pub(crate) asset: SUDTAsset, + pub(crate) distribution: SUDTDistribution, +} +impl SUDTBalancesBuilder { + pub const FIELD_COUNT: usize = 2; + pub fn asset(mut self, v: SUDTAsset) -> Self { + self.asset = v; + self + } + pub fn distribution(mut self, v: SUDTDistribution) -> Self { + self.distribution = v; + self + } +} +impl molecule::prelude::Builder for SUDTBalancesBuilder { + type Entity = SUDTBalances; + const NAME: &'static str = "SUDTBalancesBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.asset.as_slice().len() + + self.distribution.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.asset.as_slice().len(); + offsets.push(total_size); + total_size += self.distribution.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.asset.as_slice())?; + writer.write_all(self.distribution.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + SUDTBalances::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct Balances(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Balances { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Balances { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Balances { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "ckbytes", self.ckbytes())?; + write!(f, ", {}: {}", "sudts", self.sudts())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for Balances { + fn default() -> Self { + let v: Vec = vec![ + 32, 0, 0, 0, 12, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, + ]; + Balances::new_unchecked(v.into()) + } +} +impl Balances { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn ckbytes(&self) -> CKByteDistribution { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + CKByteDistribution::new_unchecked(self.0.slice(start..end)) + } + pub fn sudts(&self) -> SUDTAllocation { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + SUDTAllocation::new_unchecked(self.0.slice(start..end)) + } else { + SUDTAllocation::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> BalancesReader<'r> { + BalancesReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Balances { + type Builder = BalancesBuilder; + const NAME: &'static str = "Balances"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Balances(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BalancesReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BalancesReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .ckbytes(self.ckbytes()) + .sudts(self.sudts()) + } +} +#[derive(Clone, Copy)] +pub struct BalancesReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for BalancesReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for BalancesReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for BalancesReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "ckbytes", self.ckbytes())?; + write!(f, ", {}: {}", "sudts", self.sudts())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> BalancesReader<'r> { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn ckbytes(&self) -> CKByteDistributionReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + CKByteDistributionReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn sudts(&self) -> SUDTAllocationReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + SUDTAllocationReader::new_unchecked(&self.as_slice()[start..end]) + } else { + SUDTAllocationReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for BalancesReader<'r> { + type Entity = Balances; + const NAME: &'static str = "BalancesReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + BalancesReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + CKByteDistributionReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + SUDTAllocationReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct BalancesBuilder { + pub(crate) ckbytes: CKByteDistribution, + pub(crate) sudts: SUDTAllocation, +} +impl BalancesBuilder { + pub const FIELD_COUNT: usize = 2; + pub fn ckbytes(mut self, v: CKByteDistribution) -> Self { + self.ckbytes = v; + self + } + pub fn sudts(mut self, v: SUDTAllocation) -> Self { + self.sudts = v; + self + } +} +impl molecule::prelude::Builder for BalancesBuilder { + type Entity = Balances; + const NAME: &'static str = "BalancesBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.ckbytes.as_slice().len() + + self.sudts.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.ckbytes.as_slice().len(); + offsets.push(total_size); + total_size += self.sudts.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.ckbytes.as_slice())?; + writer.write_all(self.sudts.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Balances::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct True(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for True { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for True { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for True { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for True { + fn default() -> Self { + let v: Vec = vec![0]; + True::new_unchecked(v.into()) + } +} +impl True { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> TrueReader<'r> { + TrueReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for True { + type Builder = TrueBuilder; + const NAME: &'static str = "True"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + True(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TrueReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TrueReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0()]) + } +} +#[derive(Clone, Copy)] +pub struct TrueReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for TrueReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for TrueReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for TrueReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> TrueReader<'r> { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for TrueReader<'r> { + type Entity = True; + const NAME: &'static str = "TrueReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + TrueReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct TrueBuilder(pub(crate) [Byte; 1]); +impl ::core::fmt::Debug for TrueBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for TrueBuilder { + fn default() -> Self { + TrueBuilder([Byte::default()]) + } +} +impl TrueBuilder { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn set(mut self, v: [Byte; 1]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } +} +impl molecule::prelude::Builder for TrueBuilder { + type Entity = True; + const NAME: &'static str = "TrueBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + True::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct False(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for False { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for False { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for False { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for False { + fn default() -> Self { + let v: Vec = vec![0]; + False::new_unchecked(v.into()) + } +} +impl False { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> FalseReader<'r> { + FalseReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for False { + type Builder = FalseBuilder; + const NAME: &'static str = "False"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + False(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + FalseReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + FalseReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0()]) + } +} +#[derive(Clone, Copy)] +pub struct FalseReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for FalseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for FalseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for FalseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> FalseReader<'r> { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for FalseReader<'r> { + type Entity = False; + const NAME: &'static str = "FalseReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + FalseReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct FalseBuilder(pub(crate) [Byte; 1]); +impl ::core::fmt::Debug for FalseBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for FalseBuilder { + fn default() -> Self { + FalseBuilder([Byte::default()]) + } +} +impl FalseBuilder { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn set(mut self, v: [Byte; 1]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } +} +impl molecule::prelude::Builder for FalseBuilder { + type Entity = False; + const NAME: &'static str = "FalseBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + False::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct Bool(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Bool { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Bool { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Bool { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl ::core::default::Default for Bool { + fn default() -> Self { + let v: Vec = vec![0, 0, 0, 0, 0]; + Bool::new_unchecked(v.into()) + } +} +impl Bool { + pub const ITEMS_COUNT: usize = 2; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> BoolUnion { + let inner = self.0.slice(molecule::NUMBER_SIZE..); + match self.item_id() { + 0 => True::new_unchecked(inner).into(), + 1 => False::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } + pub fn as_reader<'r>(&'r self) -> BoolReader<'r> { + BoolReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Bool { + type Builder = BoolBuilder; + const NAME: &'static str = "Bool"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Bool(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BoolReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BoolReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_enum()) + } +} +#[derive(Clone, Copy)] +pub struct BoolReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for BoolReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for BoolReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for BoolReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl<'r> BoolReader<'r> { + pub const ITEMS_COUNT: usize = 2; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> BoolUnionReader<'r> { + let inner = &self.as_slice()[molecule::NUMBER_SIZE..]; + match self.item_id() { + 0 => TrueReader::new_unchecked(inner).into(), + 1 => FalseReader::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } +} +impl<'r> molecule::prelude::Reader<'r> for BoolReader<'r> { + type Entity = Bool; + const NAME: &'static str = "BoolReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + BoolReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let item_id = molecule::unpack_number(slice); + let inner_slice = &slice[molecule::NUMBER_SIZE..]; + match item_id { + 0 => TrueReader::verify(inner_slice, compatible), + 1 => FalseReader::verify(inner_slice, compatible), + _ => ve!(Self, UnknownItem, Self::ITEMS_COUNT, item_id), + }?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct BoolBuilder(pub(crate) BoolUnion); +impl BoolBuilder { + pub const ITEMS_COUNT: usize = 2; + pub fn set(mut self, v: I) -> Self + where + I: ::core::convert::Into, + { + self.0 = v.into(); + self + } +} +impl molecule::prelude::Builder for BoolBuilder { + type Entity = Bool; + const NAME: &'static str = "BoolBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE + self.0.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(&molecule::pack_number(self.0.item_id()))?; + writer.write_all(self.0.as_slice()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Bool::new_unchecked(inner.into()) + } +} +#[derive(Debug, Clone)] +pub enum BoolUnion { + True(True), + False(False), +} +#[derive(Debug, Clone, Copy)] +pub enum BoolUnionReader<'r> { + True(TrueReader<'r>), + False(FalseReader<'r>), +} +impl ::core::default::Default for BoolUnion { + fn default() -> Self { + BoolUnion::True(::core::default::Default::default()) + } +} +impl ::core::fmt::Display for BoolUnion { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + BoolUnion::True(ref item) => { + write!(f, "{}::{}({})", Self::NAME, True::NAME, item) + } + BoolUnion::False(ref item) => { + write!(f, "{}::{}({})", Self::NAME, False::NAME, item) + } + } + } +} +impl<'r> ::core::fmt::Display for BoolUnionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + BoolUnionReader::True(ref item) => { + write!(f, "{}::{}({})", Self::NAME, True::NAME, item) + } + BoolUnionReader::False(ref item) => { + write!(f, "{}::{}({})", Self::NAME, False::NAME, item) + } + } + } +} +impl BoolUnion { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + BoolUnion::True(ref item) => write!(f, "{}", item), + BoolUnion::False(ref item) => write!(f, "{}", item), + } + } +} +impl<'r> BoolUnionReader<'r> { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + BoolUnionReader::True(ref item) => write!(f, "{}", item), + BoolUnionReader::False(ref item) => write!(f, "{}", item), + } + } +} +impl ::core::convert::From for BoolUnion { + fn from(item: True) -> Self { + BoolUnion::True(item) + } +} +impl ::core::convert::From for BoolUnion { + fn from(item: False) -> Self { + BoolUnion::False(item) + } +} +impl<'r> ::core::convert::From> for BoolUnionReader<'r> { + fn from(item: TrueReader<'r>) -> Self { + BoolUnionReader::True(item) + } +} +impl<'r> ::core::convert::From> for BoolUnionReader<'r> { + fn from(item: FalseReader<'r>) -> Self { + BoolUnionReader::False(item) + } +} +impl BoolUnion { + pub const NAME: &'static str = "BoolUnion"; + pub fn as_bytes(&self) -> molecule::bytes::Bytes { + match self { + BoolUnion::True(item) => item.as_bytes(), + BoolUnion::False(item) => item.as_bytes(), + } + } + pub fn as_slice(&self) -> &[u8] { + match self { + BoolUnion::True(item) => item.as_slice(), + BoolUnion::False(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + BoolUnion::True(_) => 0, + BoolUnion::False(_) => 1, + } + } + pub fn item_name(&self) -> &str { + match self { + BoolUnion::True(_) => "True", + BoolUnion::False(_) => "False", + } + } + pub fn as_reader<'r>(&'r self) -> BoolUnionReader<'r> { + match self { + BoolUnion::True(item) => item.as_reader().into(), + BoolUnion::False(item) => item.as_reader().into(), + } + } +} +impl<'r> BoolUnionReader<'r> { + pub const NAME: &'r str = "BoolUnionReader"; + pub fn as_slice(&self) -> &'r [u8] { + match self { + BoolUnionReader::True(item) => item.as_slice(), + BoolUnionReader::False(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + BoolUnionReader::True(_) => 0, + BoolUnionReader::False(_) => 1, + } + } + pub fn item_name(&self) -> &str { + match self { + BoolUnionReader::True(_) => "True", + BoolUnionReader::False(_) => "False", + } + } +} +#[derive(Clone)] +pub struct A(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for A { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for A { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for A { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for A { + fn default() -> Self { + let v: Vec = vec![0]; + A::new_unchecked(v.into()) + } +} +impl A { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> AReader<'r> { + AReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for A { + type Builder = ABuilder; + const NAME: &'static str = "A"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + A(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + AReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + AReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0()]) + } +} +#[derive(Clone, Copy)] +pub struct AReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for AReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for AReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for AReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> AReader<'r> { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for AReader<'r> { + type Entity = A; + const NAME: &'static str = "AReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + AReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct ABuilder(pub(crate) [Byte; 1]); +impl ::core::fmt::Debug for ABuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for ABuilder { + fn default() -> Self { + ABuilder([Byte::default()]) + } +} +impl ABuilder { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn set(mut self, v: [Byte; 1]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } +} +impl molecule::prelude::Builder for ABuilder { + type Entity = A; + const NAME: &'static str = "ABuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + A::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct B(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for B { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for B { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for B { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for B { + fn default() -> Self { + let v: Vec = vec![0]; + B::new_unchecked(v.into()) + } +} +impl B { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> BReader<'r> { + BReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for B { + type Builder = BBuilder; + const NAME: &'static str = "B"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + B(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0()]) + } +} +#[derive(Clone, Copy)] +pub struct BReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for BReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for BReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for BReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> BReader<'r> { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for BReader<'r> { + type Entity = B; + const NAME: &'static str = "BReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + BReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct BBuilder(pub(crate) [Byte; 1]); +impl ::core::fmt::Debug for BBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for BBuilder { + fn default() -> Self { + BBuilder([Byte::default()]) + } +} +impl BBuilder { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn set(mut self, v: [Byte; 1]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } +} +impl molecule::prelude::Builder for BBuilder { + type Entity = B; + const NAME: &'static str = "BBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + B::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct App(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for App { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for App { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for App { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl ::core::default::Default for App { + fn default() -> Self { + let v: Vec = vec![]; + App::new_unchecked(v.into()) + } +} +impl App { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option { + if self.is_none() { + None + } else { + Some(Bytes::new_unchecked(self.0.clone())) + } + } + pub fn as_reader<'r>(&'r self) -> AppReader<'r> { + AppReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for App { + type Builder = AppBuilder; + const NAME: &'static str = "App"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + App(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + AppReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + AppReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_opt()) + } +} +#[derive(Clone, Copy)] +pub struct AppReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for AppReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for AppReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for AppReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl<'r> AppReader<'r> { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option> { + if self.is_none() { + None + } else { + Some(BytesReader::new_unchecked(self.as_slice())) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for AppReader<'r> { + type Entity = App; + const NAME: &'static str = "AppReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + AppReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + if !slice.is_empty() { + BytesReader::verify(&slice[..], compatible)?; + } + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct AppBuilder(pub(crate) Option); +impl AppBuilder { + pub fn set(mut self, v: Option) -> Self { + self.0 = v; + self + } +} +impl molecule::prelude::Builder for AppBuilder { + type Entity = App; + const NAME: &'static str = "AppBuilder"; + fn expected_length(&self) -> usize { + self.0 + .as_ref() + .map(|ref inner| inner.as_slice().len()) + .unwrap_or(0) + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + self.0 + .as_ref() + .map(|ref inner| writer.write_all(inner.as_slice())) + .unwrap_or(Ok(())) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + App::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct Participant(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Participant { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Participant { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Participant { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!( + f, + "{}: {}", + "payment_script_hash", + self.payment_script_hash() + )?; + write!( + f, + ", {}: {}", + "payment_min_capacity", + self.payment_min_capacity() + )?; + write!( + f, + ", {}: {}", + "unlock_script_hash", + self.unlock_script_hash() + )?; + write!(f, ", {}: {}", "pub_key", self.pub_key())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for Participant { + fn default() -> Self { + let v: Vec = vec![ + 125, 0, 0, 0, 20, 0, 0, 0, 52, 0, 0, 0, 60, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + Participant::new_unchecked(v.into()) + } +} +impl Participant { + pub const FIELD_COUNT: usize = 4; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn payment_script_hash(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn payment_min_capacity(&self) -> Uint64 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Uint64::new_unchecked(self.0.slice(start..end)) + } + pub fn unlock_script_hash(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn pub_key(&self) -> SEC1EncodedPubKey { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[20..]) as usize; + SEC1EncodedPubKey::new_unchecked(self.0.slice(start..end)) + } else { + SEC1EncodedPubKey::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> ParticipantReader<'r> { + ParticipantReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Participant { + type Builder = ParticipantBuilder; + const NAME: &'static str = "Participant"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Participant(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ParticipantReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ParticipantReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .payment_script_hash(self.payment_script_hash()) + .payment_min_capacity(self.payment_min_capacity()) + .unlock_script_hash(self.unlock_script_hash()) + .pub_key(self.pub_key()) + } +} +#[derive(Clone, Copy)] +pub struct ParticipantReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ParticipantReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ParticipantReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ParticipantReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!( + f, + "{}: {}", + "payment_script_hash", + self.payment_script_hash() + )?; + write!( + f, + ", {}: {}", + "payment_min_capacity", + self.payment_min_capacity() + )?; + write!( + f, + ", {}: {}", + "unlock_script_hash", + self.unlock_script_hash() + )?; + write!(f, ", {}: {}", "pub_key", self.pub_key())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> ParticipantReader<'r> { + pub const FIELD_COUNT: usize = 4; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn payment_script_hash(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn payment_min_capacity(&self) -> Uint64Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Uint64Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn unlock_script_hash(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn pub_key(&self) -> SEC1EncodedPubKeyReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[20..]) as usize; + SEC1EncodedPubKeyReader::new_unchecked(&self.as_slice()[start..end]) + } else { + SEC1EncodedPubKeyReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ParticipantReader<'r> { + type Entity = Participant; + const NAME: &'static str = "ParticipantReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ParticipantReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + Byte32Reader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + Uint64Reader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Byte32Reader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + SEC1EncodedPubKeyReader::verify(&slice[offsets[3]..offsets[4]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct ParticipantBuilder { + pub(crate) payment_script_hash: Byte32, + pub(crate) payment_min_capacity: Uint64, + pub(crate) unlock_script_hash: Byte32, + pub(crate) pub_key: SEC1EncodedPubKey, +} +impl ParticipantBuilder { + pub const FIELD_COUNT: usize = 4; + pub fn payment_script_hash(mut self, v: Byte32) -> Self { + self.payment_script_hash = v; + self + } + pub fn payment_min_capacity(mut self, v: Uint64) -> Self { + self.payment_min_capacity = v; + self + } + pub fn unlock_script_hash(mut self, v: Byte32) -> Self { + self.unlock_script_hash = v; + self + } + pub fn pub_key(mut self, v: SEC1EncodedPubKey) -> Self { + self.pub_key = v; + self + } +} +impl molecule::prelude::Builder for ParticipantBuilder { + type Entity = Participant; + const NAME: &'static str = "ParticipantBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.payment_script_hash.as_slice().len() + + self.payment_min_capacity.as_slice().len() + + self.unlock_script_hash.as_slice().len() + + self.pub_key.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.payment_script_hash.as_slice().len(); + offsets.push(total_size); + total_size += self.payment_min_capacity.as_slice().len(); + offsets.push(total_size); + total_size += self.unlock_script_hash.as_slice().len(); + offsets.push(total_size); + total_size += self.pub_key.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.payment_script_hash.as_slice())?; + writer.write_all(self.payment_min_capacity.as_slice())?; + writer.write_all(self.unlock_script_hash.as_slice())?; + writer.write_all(self.pub_key.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Participant::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ChannelParameters(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelParameters { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelParameters { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelParameters { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "party_a", self.party_a())?; + write!(f, ", {}: {}", "party_b", self.party_b())?; + write!(f, ", {}: {}", "nonce", self.nonce())?; + write!( + f, + ", {}: {}", + "challenge_duration", + self.challenge_duration() + )?; + write!(f, ", {}: {}", "app", self.app())?; + write!(f, ", {}: {}", "is_ledger_channel", self.is_ledger_channel())?; + write!( + f, + ", {}: {}", + "is_virtual_channel", + self.is_virtual_channel() + )?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelParameters { + fn default() -> Self { + let v: Vec = vec![ + 76, 1, 0, 0, 32, 0, 0, 0, 157, 0, 0, 0, 26, 1, 0, 0, 58, 1, 0, 0, 66, 1, 0, 0, 66, 1, + 0, 0, 71, 1, 0, 0, 125, 0, 0, 0, 20, 0, 0, 0, 52, 0, 0, 0, 60, 0, 0, 0, 92, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 20, 0, 0, 0, 52, 0, 0, + 0, 60, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + ChannelParameters::new_unchecked(v.into()) + } +} +impl ChannelParameters { + pub const FIELD_COUNT: usize = 7; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn party_a(&self) -> Participant { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Participant::new_unchecked(self.0.slice(start..end)) + } + pub fn party_b(&self) -> Participant { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Participant::new_unchecked(self.0.slice(start..end)) + } + pub fn nonce(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn challenge_duration(&self) -> Uint64 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + Uint64::new_unchecked(self.0.slice(start..end)) + } + pub fn app(&self) -> App { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + App::new_unchecked(self.0.slice(start..end)) + } + pub fn is_ledger_channel(&self) -> Bool { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + let end = molecule::unpack_number(&slice[28..]) as usize; + Bool::new_unchecked(self.0.slice(start..end)) + } + pub fn is_virtual_channel(&self) -> Bool { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[28..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[32..]) as usize; + Bool::new_unchecked(self.0.slice(start..end)) + } else { + Bool::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelParametersReader<'r> { + ChannelParametersReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelParameters { + type Builder = ChannelParametersBuilder; + const NAME: &'static str = "ChannelParameters"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelParameters(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelParametersReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelParametersReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .party_a(self.party_a()) + .party_b(self.party_b()) + .nonce(self.nonce()) + .challenge_duration(self.challenge_duration()) + .app(self.app()) + .is_ledger_channel(self.is_ledger_channel()) + .is_virtual_channel(self.is_virtual_channel()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelParametersReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelParametersReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelParametersReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelParametersReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "party_a", self.party_a())?; + write!(f, ", {}: {}", "party_b", self.party_b())?; + write!(f, ", {}: {}", "nonce", self.nonce())?; + write!( + f, + ", {}: {}", + "challenge_duration", + self.challenge_duration() + )?; + write!(f, ", {}: {}", "app", self.app())?; + write!(f, ", {}: {}", "is_ledger_channel", self.is_ledger_channel())?; + write!( + f, + ", {}: {}", + "is_virtual_channel", + self.is_virtual_channel() + )?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> ChannelParametersReader<'r> { + pub const FIELD_COUNT: usize = 7; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn party_a(&self) -> ParticipantReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ParticipantReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn party_b(&self) -> ParticipantReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + ParticipantReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn nonce(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn challenge_duration(&self) -> Uint64Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + Uint64Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn app(&self) -> AppReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + AppReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn is_ledger_channel(&self) -> BoolReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + let end = molecule::unpack_number(&slice[28..]) as usize; + BoolReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn is_virtual_channel(&self) -> BoolReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[28..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[32..]) as usize; + BoolReader::new_unchecked(&self.as_slice()[start..end]) + } else { + BoolReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelParametersReader<'r> { + type Entity = ChannelParameters; + const NAME: &'static str = "ChannelParametersReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelParametersReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + ParticipantReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + ParticipantReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Byte32Reader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + Uint64Reader::verify(&slice[offsets[3]..offsets[4]], compatible)?; + AppReader::verify(&slice[offsets[4]..offsets[5]], compatible)?; + BoolReader::verify(&slice[offsets[5]..offsets[6]], compatible)?; + BoolReader::verify(&slice[offsets[6]..offsets[7]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct ChannelParametersBuilder { + pub(crate) party_a: Participant, + pub(crate) party_b: Participant, + pub(crate) nonce: Byte32, + pub(crate) challenge_duration: Uint64, + pub(crate) app: App, + pub(crate) is_ledger_channel: Bool, + pub(crate) is_virtual_channel: Bool, +} +impl ChannelParametersBuilder { + pub const FIELD_COUNT: usize = 7; + pub fn party_a(mut self, v: Participant) -> Self { + self.party_a = v; + self + } + pub fn party_b(mut self, v: Participant) -> Self { + self.party_b = v; + self + } + pub fn nonce(mut self, v: Byte32) -> Self { + self.nonce = v; + self + } + pub fn challenge_duration(mut self, v: Uint64) -> Self { + self.challenge_duration = v; + self + } + pub fn app(mut self, v: App) -> Self { + self.app = v; + self + } + pub fn is_ledger_channel(mut self, v: Bool) -> Self { + self.is_ledger_channel = v; + self + } + pub fn is_virtual_channel(mut self, v: Bool) -> Self { + self.is_virtual_channel = v; + self + } +} +impl molecule::prelude::Builder for ChannelParametersBuilder { + type Entity = ChannelParameters; + const NAME: &'static str = "ChannelParametersBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.party_a.as_slice().len() + + self.party_b.as_slice().len() + + self.nonce.as_slice().len() + + self.challenge_duration.as_slice().len() + + self.app.as_slice().len() + + self.is_ledger_channel.as_slice().len() + + self.is_virtual_channel.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.party_a.as_slice().len(); + offsets.push(total_size); + total_size += self.party_b.as_slice().len(); + offsets.push(total_size); + total_size += self.nonce.as_slice().len(); + offsets.push(total_size); + total_size += self.challenge_duration.as_slice().len(); + offsets.push(total_size); + total_size += self.app.as_slice().len(); + offsets.push(total_size); + total_size += self.is_ledger_channel.as_slice().len(); + offsets.push(total_size); + total_size += self.is_virtual_channel.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.party_a.as_slice())?; + writer.write_all(self.party_b.as_slice())?; + writer.write_all(self.nonce.as_slice())?; + writer.write_all(self.challenge_duration.as_slice())?; + writer.write_all(self.app.as_slice())?; + writer.write_all(self.is_ledger_channel.as_slice())?; + writer.write_all(self.is_virtual_channel.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelParameters::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ChannelConstants(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelConstants { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelConstants { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelConstants { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "params", self.params())?; + write!(f, ", {}: {}", "pfls_code_hash", self.pfls_code_hash())?; + write!(f, ", {}: {}", "pfls_hash_type", self.pfls_hash_type())?; + write!(f, ", {}: {}", "pfls_min_capacity", self.pfls_min_capacity())?; + write!(f, ", {}: {}", "pcls_code_hash", self.pcls_code_hash())?; + write!(f, ", {}: {}", "pcls_hash_type", self.pcls_hash_type())?; + write!(f, ", {}: {}", "thread_token", self.thread_token())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelConstants { + fn default() -> Self { + let v: Vec = vec![ + 218, 1, 0, 0, 32, 0, 0, 0, 108, 1, 0, 0, 140, 1, 0, 0, 141, 1, 0, 0, 149, 1, 0, 0, 181, + 1, 0, 0, 182, 1, 0, 0, 76, 1, 0, 0, 32, 0, 0, 0, 157, 0, 0, 0, 26, 1, 0, 0, 58, 1, 0, + 0, 66, 1, 0, 0, 66, 1, 0, 0, 71, 1, 0, 0, 125, 0, 0, 0, 20, 0, 0, 0, 52, 0, 0, 0, 60, + 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 0, + 0, 20, 0, 0, 0, 52, 0, 0, 0, 60, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + ChannelConstants::new_unchecked(v.into()) + } +} +impl ChannelConstants { + pub const FIELD_COUNT: usize = 7; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn params(&self) -> ChannelParameters { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ChannelParameters::new_unchecked(self.0.slice(start..end)) + } + pub fn pfls_code_hash(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn pfls_hash_type(&self) -> Byte { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + Byte::new_unchecked(self.0.slice(start..end)) + } + pub fn pfls_min_capacity(&self) -> Uint64 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + Uint64::new_unchecked(self.0.slice(start..end)) + } + pub fn pcls_code_hash(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn pcls_hash_type(&self) -> Byte { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + let end = molecule::unpack_number(&slice[28..]) as usize; + Byte::new_unchecked(self.0.slice(start..end)) + } + pub fn thread_token(&self) -> ChannelToken { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[28..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[32..]) as usize; + ChannelToken::new_unchecked(self.0.slice(start..end)) + } else { + ChannelToken::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelConstantsReader<'r> { + ChannelConstantsReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelConstants { + type Builder = ChannelConstantsBuilder; + const NAME: &'static str = "ChannelConstants"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelConstants(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelConstantsReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelConstantsReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .params(self.params()) + .pfls_code_hash(self.pfls_code_hash()) + .pfls_hash_type(self.pfls_hash_type()) + .pfls_min_capacity(self.pfls_min_capacity()) + .pcls_code_hash(self.pcls_code_hash()) + .pcls_hash_type(self.pcls_hash_type()) + .thread_token(self.thread_token()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelConstantsReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelConstantsReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelConstantsReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelConstantsReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "params", self.params())?; + write!(f, ", {}: {}", "pfls_code_hash", self.pfls_code_hash())?; + write!(f, ", {}: {}", "pfls_hash_type", self.pfls_hash_type())?; + write!(f, ", {}: {}", "pfls_min_capacity", self.pfls_min_capacity())?; + write!(f, ", {}: {}", "pcls_code_hash", self.pcls_code_hash())?; + write!(f, ", {}: {}", "pcls_hash_type", self.pcls_hash_type())?; + write!(f, ", {}: {}", "thread_token", self.thread_token())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> ChannelConstantsReader<'r> { + pub const FIELD_COUNT: usize = 7; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn params(&self) -> ChannelParametersReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ChannelParametersReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn pfls_code_hash(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn pfls_hash_type(&self) -> ByteReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + ByteReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn pfls_min_capacity(&self) -> Uint64Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + Uint64Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn pcls_code_hash(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn pcls_hash_type(&self) -> ByteReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + let end = molecule::unpack_number(&slice[28..]) as usize; + ByteReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn thread_token(&self) -> ChannelTokenReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[28..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[32..]) as usize; + ChannelTokenReader::new_unchecked(&self.as_slice()[start..end]) + } else { + ChannelTokenReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelConstantsReader<'r> { + type Entity = ChannelConstants; + const NAME: &'static str = "ChannelConstantsReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelConstantsReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + ChannelParametersReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + Byte32Reader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + ByteReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + Uint64Reader::verify(&slice[offsets[3]..offsets[4]], compatible)?; + Byte32Reader::verify(&slice[offsets[4]..offsets[5]], compatible)?; + ByteReader::verify(&slice[offsets[5]..offsets[6]], compatible)?; + ChannelTokenReader::verify(&slice[offsets[6]..offsets[7]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct ChannelConstantsBuilder { + pub(crate) params: ChannelParameters, + pub(crate) pfls_code_hash: Byte32, + pub(crate) pfls_hash_type: Byte, + pub(crate) pfls_min_capacity: Uint64, + pub(crate) pcls_code_hash: Byte32, + pub(crate) pcls_hash_type: Byte, + pub(crate) thread_token: ChannelToken, +} +impl ChannelConstantsBuilder { + pub const FIELD_COUNT: usize = 7; + pub fn params(mut self, v: ChannelParameters) -> Self { + self.params = v; + self + } + pub fn pfls_code_hash(mut self, v: Byte32) -> Self { + self.pfls_code_hash = v; + self + } + pub fn pfls_hash_type(mut self, v: Byte) -> Self { + self.pfls_hash_type = v; + self + } + pub fn pfls_min_capacity(mut self, v: Uint64) -> Self { + self.pfls_min_capacity = v; + self + } + pub fn pcls_code_hash(mut self, v: Byte32) -> Self { + self.pcls_code_hash = v; + self + } + pub fn pcls_hash_type(mut self, v: Byte) -> Self { + self.pcls_hash_type = v; + self + } + pub fn thread_token(mut self, v: ChannelToken) -> Self { + self.thread_token = v; + self + } +} +impl molecule::prelude::Builder for ChannelConstantsBuilder { + type Entity = ChannelConstants; + const NAME: &'static str = "ChannelConstantsBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.params.as_slice().len() + + self.pfls_code_hash.as_slice().len() + + self.pfls_hash_type.as_slice().len() + + self.pfls_min_capacity.as_slice().len() + + self.pcls_code_hash.as_slice().len() + + self.pcls_hash_type.as_slice().len() + + self.thread_token.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.params.as_slice().len(); + offsets.push(total_size); + total_size += self.pfls_code_hash.as_slice().len(); + offsets.push(total_size); + total_size += self.pfls_hash_type.as_slice().len(); + offsets.push(total_size); + total_size += self.pfls_min_capacity.as_slice().len(); + offsets.push(total_size); + total_size += self.pcls_code_hash.as_slice().len(); + offsets.push(total_size); + total_size += self.pcls_hash_type.as_slice().len(); + offsets.push(total_size); + total_size += self.thread_token.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.params.as_slice())?; + writer.write_all(self.pfls_code_hash.as_slice())?; + writer.write_all(self.pfls_hash_type.as_slice())?; + writer.write_all(self.pfls_min_capacity.as_slice())?; + writer.write_all(self.pcls_code_hash.as_slice())?; + writer.write_all(self.pcls_hash_type.as_slice())?; + writer.write_all(self.thread_token.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelConstants::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct Fund(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Fund { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Fund { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Fund { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for Fund { + fn default() -> Self { + let v: Vec = vec![0]; + Fund::new_unchecked(v.into()) + } +} +impl Fund { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> FundReader<'r> { + FundReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Fund { + type Builder = FundBuilder; + const NAME: &'static str = "Fund"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Fund(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + FundReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + FundReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0()]) + } +} +#[derive(Clone, Copy)] +pub struct FundReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for FundReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for FundReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for FundReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> FundReader<'r> { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for FundReader<'r> { + type Entity = Fund; + const NAME: &'static str = "FundReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + FundReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct FundBuilder(pub(crate) [Byte; 1]); +impl ::core::fmt::Debug for FundBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for FundBuilder { + fn default() -> Self { + FundBuilder([Byte::default()]) + } +} +impl FundBuilder { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn set(mut self, v: [Byte; 1]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } +} +impl molecule::prelude::Builder for FundBuilder { + type Entity = Fund; + const NAME: &'static str = "FundBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Fund::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct Abort(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Abort { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Abort { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Abort { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for Abort { + fn default() -> Self { + let v: Vec = vec![0]; + Abort::new_unchecked(v.into()) + } +} +impl Abort { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> AbortReader<'r> { + AbortReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Abort { + type Builder = AbortBuilder; + const NAME: &'static str = "Abort"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Abort(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + AbortReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + AbortReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0()]) + } +} +#[derive(Clone, Copy)] +pub struct AbortReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for AbortReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for AbortReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for AbortReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> AbortReader<'r> { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for AbortReader<'r> { + type Entity = Abort; + const NAME: &'static str = "AbortReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + AbortReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct AbortBuilder(pub(crate) [Byte; 1]); +impl ::core::fmt::Debug for AbortBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for AbortBuilder { + fn default() -> Self { + AbortBuilder([Byte::default()]) + } +} +impl AbortBuilder { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn set(mut self, v: [Byte; 1]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } +} +impl molecule::prelude::Builder for AbortBuilder { + type Entity = Abort; + const NAME: &'static str = "AbortBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Abort::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct Dispute(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Dispute { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Dispute { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Dispute { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "sig_a", self.sig_a())?; + write!(f, ", {}: {}", "sig_b", self.sig_b())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for Dispute { + fn default() -> Self { + let v: Vec = vec![ + 20, 0, 0, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + Dispute::new_unchecked(v.into()) + } +} +impl Dispute { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn sig_a(&self) -> Bytes { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Bytes::new_unchecked(self.0.slice(start..end)) + } + pub fn sig_b(&self) -> Bytes { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + Bytes::new_unchecked(self.0.slice(start..end)) + } else { + Bytes::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> DisputeReader<'r> { + DisputeReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Dispute { + type Builder = DisputeBuilder; + const NAME: &'static str = "Dispute"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Dispute(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + DisputeReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + DisputeReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().sig_a(self.sig_a()).sig_b(self.sig_b()) + } +} +#[derive(Clone, Copy)] +pub struct DisputeReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for DisputeReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for DisputeReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for DisputeReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "sig_a", self.sig_a())?; + write!(f, ", {}: {}", "sig_b", self.sig_b())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> DisputeReader<'r> { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn sig_a(&self) -> BytesReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + BytesReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn sig_b(&self) -> BytesReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + BytesReader::new_unchecked(&self.as_slice()[start..end]) + } else { + BytesReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for DisputeReader<'r> { + type Entity = Dispute; + const NAME: &'static str = "DisputeReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + DisputeReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + BytesReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + BytesReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct DisputeBuilder { + pub(crate) sig_a: Bytes, + pub(crate) sig_b: Bytes, +} +impl DisputeBuilder { + pub const FIELD_COUNT: usize = 2; + pub fn sig_a(mut self, v: Bytes) -> Self { + self.sig_a = v; + self + } + pub fn sig_b(mut self, v: Bytes) -> Self { + self.sig_b = v; + self + } +} +impl molecule::prelude::Builder for DisputeBuilder { + type Entity = Dispute; + const NAME: &'static str = "DisputeBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.sig_a.as_slice().len() + + self.sig_b.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.sig_a.as_slice().len(); + offsets.push(total_size); + total_size += self.sig_b.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.sig_a.as_slice())?; + writer.write_all(self.sig_b.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Dispute::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct Close(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Close { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Close { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Close { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "state", self.state())?; + write!(f, ", {}: {}", "sig_a", self.sig_a())?; + write!(f, ", {}: {}", "sig_b", self.sig_b())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for Close { + fn default() -> Self { + let v: Vec = vec![ + 121, 0, 0, 0, 16, 0, 0, 0, 113, 0, 0, 0, 117, 0, 0, 0, 97, 0, 0, 0, 20, 0, 0, 0, 52, 0, + 0, 0, 84, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 12, 0, 0, 0, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + Close::new_unchecked(v.into()) + } +} +impl Close { + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn state(&self) -> ChannelState { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ChannelState::new_unchecked(self.0.slice(start..end)) + } + pub fn sig_a(&self) -> Bytes { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Bytes::new_unchecked(self.0.slice(start..end)) + } + pub fn sig_b(&self) -> Bytes { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + Bytes::new_unchecked(self.0.slice(start..end)) + } else { + Bytes::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> CloseReader<'r> { + CloseReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Close { + type Builder = CloseBuilder; + const NAME: &'static str = "Close"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Close(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + CloseReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + CloseReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .state(self.state()) + .sig_a(self.sig_a()) + .sig_b(self.sig_b()) + } +} +#[derive(Clone, Copy)] +pub struct CloseReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for CloseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for CloseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for CloseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "state", self.state())?; + write!(f, ", {}: {}", "sig_a", self.sig_a())?; + write!(f, ", {}: {}", "sig_b", self.sig_b())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> CloseReader<'r> { + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn state(&self) -> ChannelStateReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ChannelStateReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn sig_a(&self) -> BytesReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + BytesReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn sig_b(&self) -> BytesReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + BytesReader::new_unchecked(&self.as_slice()[start..end]) + } else { + BytesReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for CloseReader<'r> { + type Entity = Close; + const NAME: &'static str = "CloseReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + CloseReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + ChannelStateReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + BytesReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + BytesReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct CloseBuilder { + pub(crate) state: ChannelState, + pub(crate) sig_a: Bytes, + pub(crate) sig_b: Bytes, +} +impl CloseBuilder { + pub const FIELD_COUNT: usize = 3; + pub fn state(mut self, v: ChannelState) -> Self { + self.state = v; + self + } + pub fn sig_a(mut self, v: Bytes) -> Self { + self.sig_a = v; + self + } + pub fn sig_b(mut self, v: Bytes) -> Self { + self.sig_b = v; + self + } +} +impl molecule::prelude::Builder for CloseBuilder { + type Entity = Close; + const NAME: &'static str = "CloseBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.state.as_slice().len() + + self.sig_a.as_slice().len() + + self.sig_b.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.state.as_slice().len(); + offsets.push(total_size); + total_size += self.sig_a.as_slice().len(); + offsets.push(total_size); + total_size += self.sig_b.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.state.as_slice())?; + writer.write_all(self.sig_a.as_slice())?; + writer.write_all(self.sig_b.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Close::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ForceClose(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ForceClose { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ForceClose { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ForceClose { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for ForceClose { + fn default() -> Self { + let v: Vec = vec![0]; + ForceClose::new_unchecked(v.into()) + } +} +impl ForceClose { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> ForceCloseReader<'r> { + ForceCloseReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ForceClose { + type Builder = ForceCloseBuilder; + const NAME: &'static str = "ForceClose"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ForceClose(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ForceCloseReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ForceCloseReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0()]) + } +} +#[derive(Clone, Copy)] +pub struct ForceCloseReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ForceCloseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ForceCloseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ForceCloseReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> ForceCloseReader<'r> { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for ForceCloseReader<'r> { + type Entity = ForceClose; + const NAME: &'static str = "ForceCloseReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ForceCloseReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +pub struct ForceCloseBuilder(pub(crate) [Byte; 1]); +impl ::core::fmt::Debug for ForceCloseBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for ForceCloseBuilder { + fn default() -> Self { + ForceCloseBuilder([Byte::default()]) + } +} +impl ForceCloseBuilder { + pub const TOTAL_SIZE: usize = 1; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 1; + pub fn set(mut self, v: [Byte; 1]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } +} +impl molecule::prelude::Builder for ForceCloseBuilder { + type Entity = ForceClose; + const NAME: &'static str = "ForceCloseBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ForceClose::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ChannelWitness(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelWitness { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelWitness { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelWitness { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl ::core::default::Default for ChannelWitness { + fn default() -> Self { + let v: Vec = vec![0, 0, 0, 0, 0]; + ChannelWitness::new_unchecked(v.into()) + } +} +impl ChannelWitness { + pub const ITEMS_COUNT: usize = 5; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> ChannelWitnessUnion { + let inner = self.0.slice(molecule::NUMBER_SIZE..); + match self.item_id() { + 0 => Fund::new_unchecked(inner).into(), + 1 => Abort::new_unchecked(inner).into(), + 2 => Dispute::new_unchecked(inner).into(), + 3 => Close::new_unchecked(inner).into(), + 4 => ForceClose::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } + pub fn as_reader<'r>(&'r self) -> ChannelWitnessReader<'r> { + ChannelWitnessReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelWitness { + type Builder = ChannelWitnessBuilder; + const NAME: &'static str = "ChannelWitness"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelWitness(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelWitnessReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelWitnessReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_enum()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelWitnessReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelWitnessReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelWitnessReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelWitnessReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl<'r> ChannelWitnessReader<'r> { + pub const ITEMS_COUNT: usize = 5; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> ChannelWitnessUnionReader<'r> { + let inner = &self.as_slice()[molecule::NUMBER_SIZE..]; + match self.item_id() { + 0 => FundReader::new_unchecked(inner).into(), + 1 => AbortReader::new_unchecked(inner).into(), + 2 => DisputeReader::new_unchecked(inner).into(), + 3 => CloseReader::new_unchecked(inner).into(), + 4 => ForceCloseReader::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelWitnessReader<'r> { + type Entity = ChannelWitness; + const NAME: &'static str = "ChannelWitnessReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelWitnessReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let item_id = molecule::unpack_number(slice); + let inner_slice = &slice[molecule::NUMBER_SIZE..]; + match item_id { + 0 => FundReader::verify(inner_slice, compatible), + 1 => AbortReader::verify(inner_slice, compatible), + 2 => DisputeReader::verify(inner_slice, compatible), + 3 => CloseReader::verify(inner_slice, compatible), + 4 => ForceCloseReader::verify(inner_slice, compatible), + _ => ve!(Self, UnknownItem, Self::ITEMS_COUNT, item_id), + }?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct ChannelWitnessBuilder(pub(crate) ChannelWitnessUnion); +impl ChannelWitnessBuilder { + pub const ITEMS_COUNT: usize = 5; + pub fn set(mut self, v: I) -> Self + where + I: ::core::convert::Into, + { + self.0 = v.into(); + self + } +} +impl molecule::prelude::Builder for ChannelWitnessBuilder { + type Entity = ChannelWitness; + const NAME: &'static str = "ChannelWitnessBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE + self.0.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(&molecule::pack_number(self.0.item_id()))?; + writer.write_all(self.0.as_slice()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelWitness::new_unchecked(inner.into()) + } +} +#[derive(Debug, Clone)] +pub enum ChannelWitnessUnion { + Fund(Fund), + Abort(Abort), + Dispute(Dispute), + Close(Close), + ForceClose(ForceClose), +} +#[derive(Debug, Clone, Copy)] +pub enum ChannelWitnessUnionReader<'r> { + Fund(FundReader<'r>), + Abort(AbortReader<'r>), + Dispute(DisputeReader<'r>), + Close(CloseReader<'r>), + ForceClose(ForceCloseReader<'r>), +} +impl ::core::default::Default for ChannelWitnessUnion { + fn default() -> Self { + ChannelWitnessUnion::Fund(::core::default::Default::default()) + } +} +impl ::core::fmt::Display for ChannelWitnessUnion { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + ChannelWitnessUnion::Fund(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Fund::NAME, item) + } + ChannelWitnessUnion::Abort(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Abort::NAME, item) + } + ChannelWitnessUnion::Dispute(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Dispute::NAME, item) + } + ChannelWitnessUnion::Close(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Close::NAME, item) + } + ChannelWitnessUnion::ForceClose(ref item) => { + write!(f, "{}::{}({})", Self::NAME, ForceClose::NAME, item) + } + } + } +} +impl<'r> ::core::fmt::Display for ChannelWitnessUnionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + ChannelWitnessUnionReader::Fund(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Fund::NAME, item) + } + ChannelWitnessUnionReader::Abort(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Abort::NAME, item) + } + ChannelWitnessUnionReader::Dispute(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Dispute::NAME, item) + } + ChannelWitnessUnionReader::Close(ref item) => { + write!(f, "{}::{}({})", Self::NAME, Close::NAME, item) + } + ChannelWitnessUnionReader::ForceClose(ref item) => { + write!(f, "{}::{}({})", Self::NAME, ForceClose::NAME, item) + } + } + } +} +impl ChannelWitnessUnion { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + ChannelWitnessUnion::Fund(ref item) => write!(f, "{}", item), + ChannelWitnessUnion::Abort(ref item) => write!(f, "{}", item), + ChannelWitnessUnion::Dispute(ref item) => write!(f, "{}", item), + ChannelWitnessUnion::Close(ref item) => write!(f, "{}", item), + ChannelWitnessUnion::ForceClose(ref item) => write!(f, "{}", item), + } + } +} +impl<'r> ChannelWitnessUnionReader<'r> { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + ChannelWitnessUnionReader::Fund(ref item) => write!(f, "{}", item), + ChannelWitnessUnionReader::Abort(ref item) => write!(f, "{}", item), + ChannelWitnessUnionReader::Dispute(ref item) => write!(f, "{}", item), + ChannelWitnessUnionReader::Close(ref item) => write!(f, "{}", item), + ChannelWitnessUnionReader::ForceClose(ref item) => write!(f, "{}", item), + } + } +} +impl ::core::convert::From for ChannelWitnessUnion { + fn from(item: Fund) -> Self { + ChannelWitnessUnion::Fund(item) + } +} +impl ::core::convert::From for ChannelWitnessUnion { + fn from(item: Abort) -> Self { + ChannelWitnessUnion::Abort(item) + } +} +impl ::core::convert::From for ChannelWitnessUnion { + fn from(item: Dispute) -> Self { + ChannelWitnessUnion::Dispute(item) + } +} +impl ::core::convert::From for ChannelWitnessUnion { + fn from(item: Close) -> Self { + ChannelWitnessUnion::Close(item) + } +} +impl ::core::convert::From for ChannelWitnessUnion { + fn from(item: ForceClose) -> Self { + ChannelWitnessUnion::ForceClose(item) + } +} +impl<'r> ::core::convert::From> for ChannelWitnessUnionReader<'r> { + fn from(item: FundReader<'r>) -> Self { + ChannelWitnessUnionReader::Fund(item) + } +} +impl<'r> ::core::convert::From> for ChannelWitnessUnionReader<'r> { + fn from(item: AbortReader<'r>) -> Self { + ChannelWitnessUnionReader::Abort(item) + } +} +impl<'r> ::core::convert::From> for ChannelWitnessUnionReader<'r> { + fn from(item: DisputeReader<'r>) -> Self { + ChannelWitnessUnionReader::Dispute(item) + } +} +impl<'r> ::core::convert::From> for ChannelWitnessUnionReader<'r> { + fn from(item: CloseReader<'r>) -> Self { + ChannelWitnessUnionReader::Close(item) + } +} +impl<'r> ::core::convert::From> for ChannelWitnessUnionReader<'r> { + fn from(item: ForceCloseReader<'r>) -> Self { + ChannelWitnessUnionReader::ForceClose(item) + } +} +impl ChannelWitnessUnion { + pub const NAME: &'static str = "ChannelWitnessUnion"; + pub fn as_bytes(&self) -> molecule::bytes::Bytes { + match self { + ChannelWitnessUnion::Fund(item) => item.as_bytes(), + ChannelWitnessUnion::Abort(item) => item.as_bytes(), + ChannelWitnessUnion::Dispute(item) => item.as_bytes(), + ChannelWitnessUnion::Close(item) => item.as_bytes(), + ChannelWitnessUnion::ForceClose(item) => item.as_bytes(), + } + } + pub fn as_slice(&self) -> &[u8] { + match self { + ChannelWitnessUnion::Fund(item) => item.as_slice(), + ChannelWitnessUnion::Abort(item) => item.as_slice(), + ChannelWitnessUnion::Dispute(item) => item.as_slice(), + ChannelWitnessUnion::Close(item) => item.as_slice(), + ChannelWitnessUnion::ForceClose(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + ChannelWitnessUnion::Fund(_) => 0, + ChannelWitnessUnion::Abort(_) => 1, + ChannelWitnessUnion::Dispute(_) => 2, + ChannelWitnessUnion::Close(_) => 3, + ChannelWitnessUnion::ForceClose(_) => 4, + } + } + pub fn item_name(&self) -> &str { + match self { + ChannelWitnessUnion::Fund(_) => "Fund", + ChannelWitnessUnion::Abort(_) => "Abort", + ChannelWitnessUnion::Dispute(_) => "Dispute", + ChannelWitnessUnion::Close(_) => "Close", + ChannelWitnessUnion::ForceClose(_) => "ForceClose", + } + } + pub fn as_reader<'r>(&'r self) -> ChannelWitnessUnionReader<'r> { + match self { + ChannelWitnessUnion::Fund(item) => item.as_reader().into(), + ChannelWitnessUnion::Abort(item) => item.as_reader().into(), + ChannelWitnessUnion::Dispute(item) => item.as_reader().into(), + ChannelWitnessUnion::Close(item) => item.as_reader().into(), + ChannelWitnessUnion::ForceClose(item) => item.as_reader().into(), + } + } +} +impl<'r> ChannelWitnessUnionReader<'r> { + pub const NAME: &'r str = "ChannelWitnessUnionReader"; + pub fn as_slice(&self) -> &'r [u8] { + match self { + ChannelWitnessUnionReader::Fund(item) => item.as_slice(), + ChannelWitnessUnionReader::Abort(item) => item.as_slice(), + ChannelWitnessUnionReader::Dispute(item) => item.as_slice(), + ChannelWitnessUnionReader::Close(item) => item.as_slice(), + ChannelWitnessUnionReader::ForceClose(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + ChannelWitnessUnionReader::Fund(_) => 0, + ChannelWitnessUnionReader::Abort(_) => 1, + ChannelWitnessUnionReader::Dispute(_) => 2, + ChannelWitnessUnionReader::Close(_) => 3, + ChannelWitnessUnionReader::ForceClose(_) => 4, + } + } + pub fn item_name(&self) -> &str { + match self { + ChannelWitnessUnionReader::Fund(_) => "Fund", + ChannelWitnessUnionReader::Abort(_) => "Abort", + ChannelWitnessUnionReader::Dispute(_) => "Dispute", + ChannelWitnessUnionReader::Close(_) => "Close", + ChannelWitnessUnionReader::ForceClose(_) => "ForceClose", + } + } +} +#[derive(Clone)] +pub struct ChannelState(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelState { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelState { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelState { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "channel_id", self.channel_id())?; + write!(f, ", {}: {}", "balances", self.balances())?; + write!(f, ", {}: {}", "version", self.version())?; + write!(f, ", {}: {}", "is_final", self.is_final())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelState { + fn default() -> Self { + let v: Vec = vec![ + 97, 0, 0, 0, 20, 0, 0, 0, 52, 0, 0, 0, 84, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 12, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + ChannelState::new_unchecked(v.into()) + } +} +impl ChannelState { + pub const FIELD_COUNT: usize = 4; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn channel_id(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn balances(&self) -> Balances { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Balances::new_unchecked(self.0.slice(start..end)) + } + pub fn version(&self) -> Uint64 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + Uint64::new_unchecked(self.0.slice(start..end)) + } + pub fn is_final(&self) -> Bool { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[20..]) as usize; + Bool::new_unchecked(self.0.slice(start..end)) + } else { + Bool::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelStateReader<'r> { + ChannelStateReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelState { + type Builder = ChannelStateBuilder; + const NAME: &'static str = "ChannelState"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelState(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelStateReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelStateReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .channel_id(self.channel_id()) + .balances(self.balances()) + .version(self.version()) + .is_final(self.is_final()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelStateReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelStateReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelStateReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelStateReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "channel_id", self.channel_id())?; + write!(f, ", {}: {}", "balances", self.balances())?; + write!(f, ", {}: {}", "version", self.version())?; + write!(f, ", {}: {}", "is_final", self.is_final())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> ChannelStateReader<'r> { + pub const FIELD_COUNT: usize = 4; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn channel_id(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn balances(&self) -> BalancesReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + BalancesReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn version(&self) -> Uint64Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + Uint64Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn is_final(&self) -> BoolReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[20..]) as usize; + BoolReader::new_unchecked(&self.as_slice()[start..end]) + } else { + BoolReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelStateReader<'r> { + type Entity = ChannelState; + const NAME: &'static str = "ChannelStateReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelStateReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + Byte32Reader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + BalancesReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Uint64Reader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + BoolReader::verify(&slice[offsets[3]..offsets[4]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct ChannelStateBuilder { + pub(crate) channel_id: Byte32, + pub(crate) balances: Balances, + pub(crate) version: Uint64, + pub(crate) is_final: Bool, +} +impl ChannelStateBuilder { + pub const FIELD_COUNT: usize = 4; + pub fn channel_id(mut self, v: Byte32) -> Self { + self.channel_id = v; + self + } + pub fn balances(mut self, v: Balances) -> Self { + self.balances = v; + self + } + pub fn version(mut self, v: Uint64) -> Self { + self.version = v; + self + } + pub fn is_final(mut self, v: Bool) -> Self { + self.is_final = v; + self + } +} +impl molecule::prelude::Builder for ChannelStateBuilder { + type Entity = ChannelState; + const NAME: &'static str = "ChannelStateBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.channel_id.as_slice().len() + + self.balances.as_slice().len() + + self.version.as_slice().len() + + self.is_final.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.channel_id.as_slice().len(); + offsets.push(total_size); + total_size += self.balances.as_slice().len(); + offsets.push(total_size); + total_size += self.version.as_slice().len(); + offsets.push(total_size); + total_size += self.is_final.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.channel_id.as_slice())?; + writer.write_all(self.balances.as_slice())?; + writer.write_all(self.version.as_slice())?; + writer.write_all(self.is_final.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelState::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ChannelStatus(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelStatus { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelStatus { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelStatus { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "state", self.state())?; + write!(f, ", {}: {}", "funded", self.funded())?; + write!(f, ", {}: {}", "disputed", self.disputed())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelStatus { + fn default() -> Self { + let v: Vec = vec![ + 123, 0, 0, 0, 16, 0, 0, 0, 113, 0, 0, 0, 118, 0, 0, 0, 97, 0, 0, 0, 20, 0, 0, 0, 52, 0, + 0, 0, 84, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 12, 0, 0, 0, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + ChannelStatus::new_unchecked(v.into()) + } +} +impl ChannelStatus { + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn state(&self) -> ChannelState { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ChannelState::new_unchecked(self.0.slice(start..end)) + } + pub fn funded(&self) -> Bool { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Bool::new_unchecked(self.0.slice(start..end)) + } + pub fn disputed(&self) -> Bool { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + Bool::new_unchecked(self.0.slice(start..end)) + } else { + Bool::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelStatusReader<'r> { + ChannelStatusReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelStatus { + type Builder = ChannelStatusBuilder; + const NAME: &'static str = "ChannelStatus"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelStatus(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelStatusReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelStatusReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .state(self.state()) + .funded(self.funded()) + .disputed(self.disputed()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelStatusReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelStatusReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelStatusReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelStatusReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "state", self.state())?; + write!(f, ", {}: {}", "funded", self.funded())?; + write!(f, ", {}: {}", "disputed", self.disputed())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> ChannelStatusReader<'r> { + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn state(&self) -> ChannelStateReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + ChannelStateReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn funded(&self) -> BoolReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + BoolReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn disputed(&self) -> BoolReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + BoolReader::new_unchecked(&self.as_slice()[start..end]) + } else { + BoolReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelStatusReader<'r> { + type Entity = ChannelStatus; + const NAME: &'static str = "ChannelStatusReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelStatusReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE && Self::FIELD_COUNT == 0 { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + ChannelStateReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + BoolReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + BoolReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct ChannelStatusBuilder { + pub(crate) state: ChannelState, + pub(crate) funded: Bool, + pub(crate) disputed: Bool, +} +impl ChannelStatusBuilder { + pub const FIELD_COUNT: usize = 3; + pub fn state(mut self, v: ChannelState) -> Self { + self.state = v; + self + } + pub fn funded(mut self, v: Bool) -> Self { + self.funded = v; + self + } + pub fn disputed(mut self, v: Bool) -> Self { + self.disputed = v; + self + } +} +impl molecule::prelude::Builder for ChannelStatusBuilder { + type Entity = ChannelStatus; + const NAME: &'static str = "ChannelStatusBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.state.as_slice().len() + + self.funded.as_slice().len() + + self.disputed.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.state.as_slice().len(); + offsets.push(total_size); + total_size += self.funded.as_slice().len(); + offsets.push(total_size); + total_size += self.disputed.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.state.as_slice())?; + writer.write_all(self.funded.as_slice())?; + writer.write_all(self.disputed.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelStatus::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ChannelToken(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelToken { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelToken { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelToken { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "out_point", self.out_point())?; + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelToken { + fn default() -> Self { + let v: Vec = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ]; + ChannelToken::new_unchecked(v.into()) + } +} +impl ChannelToken { + pub const TOTAL_SIZE: usize = 36; + pub const FIELD_SIZES: [usize; 1] = [36]; + pub const FIELD_COUNT: usize = 1; + pub fn out_point(&self) -> OutPoint { + OutPoint::new_unchecked(self.0.slice(0..36)) + } + pub fn as_reader<'r>(&'r self) -> ChannelTokenReader<'r> { + ChannelTokenReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelToken { + type Builder = ChannelTokenBuilder; + const NAME: &'static str = "ChannelToken"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelToken(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelTokenReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelTokenReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().out_point(self.out_point()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelTokenReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelTokenReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelTokenReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelTokenReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "out_point", self.out_point())?; + write!(f, " }}") + } +} +impl<'r> ChannelTokenReader<'r> { + pub const TOTAL_SIZE: usize = 36; + pub const FIELD_SIZES: [usize; 1] = [36]; + pub const FIELD_COUNT: usize = 1; + pub fn out_point(&self) -> OutPointReader<'r> { + OutPointReader::new_unchecked(&self.as_slice()[0..36]) + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelTokenReader<'r> { + type Entity = ChannelToken; + const NAME: &'static str = "ChannelTokenReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelTokenReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct ChannelTokenBuilder { + pub(crate) out_point: OutPoint, +} +impl ChannelTokenBuilder { + pub const TOTAL_SIZE: usize = 36; + pub const FIELD_SIZES: [usize; 1] = [36]; + pub const FIELD_COUNT: usize = 1; + pub fn out_point(mut self, v: OutPoint) -> Self { + self.out_point = v; + self + } +} +impl molecule::prelude::Builder for ChannelTokenBuilder { + type Entity = ChannelToken; + const NAME: &'static str = "ChannelTokenBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.out_point.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelToken::new_unchecked(inner.into()) + } +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/sig.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/sig.rs new file mode 100644 index 0000000..3a7e905 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/src/sig.rs @@ -0,0 +1,11 @@ +use k256::{ecdsa::{VerifyingKey, Signature, signature::{hazmat::PrehashVerifier}}, elliptic_curve::sec1::EncodedPoint, Secp256k1}; + +use crate::error::Error; + +pub fn verify_signature(msg_hash: &[u8; 32], sig: &[u8], key: &[u8]) -> Result<(), Error> { + let signature = Signature::from_der(sig)?; + let e = EncodedPoint::::from_bytes(key).expect("unable to decode public key"); + let verifying_key = VerifyingKey::from_encoded_point(&e)?; + verifying_key.verify_prehash(msg_hash, &signature)?; + Ok(()) +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-common/types.mol b/payment-channel-ckb/devnet/contracts/contracts/perun-common/types.mol new file mode 100644 index 0000000..5fa3a5f --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-common/types.mol @@ -0,0 +1,140 @@ +import blockchain; + +/* Perun Types */ +array SEC1EncodedPubKey [byte; 33]; + +array CKByteDistribution [Uint64; 2]; + +array SUDTDistribution [Uint128; 2]; + +vector SUDTAllocation ; + +table SUDTAsset { + type_script: Script, + // The max_capacity of an SUDTAsset should always be at least the capacity needed for the SUDT type script + outputs_data + // + max(party_a.payment_min_capacity, party_b.payment_min_capacity) + // Make sure verify this in the Funding Agreement, as the contract can not verify this upon channel start! + max_capacity: Uint64, +} + +table SUDTBalances { + asset: SUDTAsset, + distribution: SUDTDistribution, +} + +table Balances { + ckbytes: CKByteDistribution, + sudts: SUDTAllocation, +} + +array True [byte; 1]; +array False [byte; 1]; + +union Bool { + True, + False, +} + +array A [byte; 1]; +array B [byte; 1]; + + +option App (Bytes); + +// Terminology: +// - script_hash: By script_hash we mean the results of the syscalls load_cell_lock_hash / load_cell_type_hash +// and the sdk function calc_script_hash. This is the hash of the script struct (code_hash, hash_type and args). +// - code_hash: By code_hash we mean the member of a script that hold the hash of the executed code (depending on the hash_type). +// See: https://docs.nervos.org/docs/reference/script/ + + + +table Participant { + // payment_script_hash specifies the script-hash used + // to lock payments to this participant (upon channel close) + payment_script_hash: Byte32, + // payment_min_capacity specifies the minimum capacity of the payment lock script. + payment_min_capacity: Uint64, + + // unlock_script_hash specifies the script-hash that needs to be present in the inputs + // to a transaction to authorize the transaction to interact with the channel as + // this channel participant. + unlock_script_hash: Byte32, + + pub_key: SEC1EncodedPubKey, +} +table ChannelParameters { + party_a: Participant, + party_b: Participant, + nonce: Byte32, + challenge_duration: Uint64, + // The default should be NoApp! + app: App, + // This should always be set to true for, as we currently only support ledger channels. + is_ledger_channel: Bool, + // This should always be set to false for, as we currently do not support virtual channels. + is_virtual_channel: Bool, +} + +// Important: Upon channel creation, every participant must verify the integrity of the channel. +// This includes verifying that the correct ChannelConstants are present. +// If e.g. the payment_min_capacity (inside the participants of the channel parameters) were to be significantly larger than the minimum +// capacity of the payment lock script, a party could steal funds from the channel participants with balances smaller than the +// payment_min_capacity upon channel closing. +table ChannelConstants { + params: ChannelParameters, + // pfls__code_hash specifies the code hash of the lock_script that guards funds for this channel. + // Specifically, this should be the perun-funds-lockscript. + pfls_code_hash: Byte32, + pfls_hash_type: byte, + pfls_min_capacity: Uint64, + + // pcls_hash specifies the lock_script used for this channel. + // Specifically, this should be the perun-channel-lockscript. + pcls_code_hash: Byte32, + pcls_hash_type: byte, + + thread_token: ChannelToken, +} + +array Fund [byte; 1]; + +array Abort [byte; 1]; + +table Dispute { + sig_a: Bytes, + sig_b: Bytes, +} +table Close { + state: ChannelState, + sig_a: Bytes, + sig_b: Bytes, +} +array ForceClose [byte; 1]; + + +union ChannelWitness { + Fund, + Abort, + Dispute, + Close, + ForceClose, +} + +table ChannelState { + // + channel_id: Byte32, + balances: Balances, + version: Uint64, + is_final: Bool, +} + +table ChannelStatus { + state: ChannelState, + funded: Bool, + disputed: Bool, +} + +struct ChannelToken { + out_point: OutPoint, +} \ No newline at end of file diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/Cargo.toml b/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/Cargo.toml new file mode 100644 index 0000000..6e66cd6 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "perun-funds-lockscript" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ckb-std = "0.10.0" +perun-common = { path = "../perun-common", default-features = false, features = ["contract"] } diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/entry.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/entry.rs new file mode 100644 index 0000000..1d21352 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/entry.rs @@ -0,0 +1,47 @@ +// Import from `core` instead of from `std` since we are in no-std mode +use core::result::Result; + +// Import heap related library from `alloc` +// https://doc.rust-lang.org/alloc/index.html + +use perun_common::error::Error; + +// Import CKB syscalls and structures +// https://docs.rs/ckb-std/ +use ckb_std::{ + ckb_constants::Source, + ckb_types::{bytes::Bytes, packed::Byte32, prelude::*}, + high_level::{load_cell_type_hash, load_script, load_transaction}, +}; + +// The Perun Funds Lock Script can be unlocked by including an input cell with the pcts script hash +// that is specified in the args of the pfls. +pub fn main() -> Result<(), Error> { + let script = load_script()?; + let args: Bytes = script.args().unpack(); + + if args.is_empty() { + return Err(Error::NoArgs); + } + + let pcts_script_hash = Byte32::from_slice(&args)?; + + return verify_pcts_in_inputs(&pcts_script_hash.unpack()); +} + +pub fn verify_pcts_in_inputs(pcts_script_hash: &[u8; 32]) -> Result<(), Error> { + let num_inputs = load_transaction()?.raw().inputs().len(); + for i in 0..num_inputs { + match load_cell_type_hash(i, Source::Input)? { + Some(cell_type_script_hash) => { + if cell_type_script_hash[..] == pcts_script_hash[..] { + return Ok(()); + } else { + continue; + } + } + None => continue, + }; + } + Err(Error::PCTSNotFound) +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/main.rs b/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/main.rs new file mode 100644 index 0000000..9306fc4 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/perun-funds-lockscript/src/main.rs @@ -0,0 +1,32 @@ +//! Generated by capsule +//! +//! `main.rs` is used to define rust lang items and modules. +//! See `entry.rs` for the `main` function. +//! See `error.rs` for the `Error` type. + +#![no_std] +#![no_main] +#![feature(asm_sym)] +#![feature(lang_items)] +#![feature(alloc_error_handler)] +#![feature(panic_info_message)] + +// define modules +mod entry; + +use ckb_std::default_alloc; +use core::arch::asm; + +ckb_std::entry!(program_entry); +default_alloc!(); + +/// program entry +/// +/// Both `argc` and `argv` can be omitted. +fn program_entry(_argc: u64, _argv: *const *const u8) -> i8 { + // Call main function and return error code + match entry::main() { + Ok(_) => 0, + Err(err) => err as i8, + } +} diff --git a/payment-channel-ckb/devnet/contracts/contracts/sample-udt/Cargo.toml b/payment-channel-ckb/devnet/contracts/contracts/sample-udt/Cargo.toml new file mode 100644 index 0000000..1d27e95 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/sample-udt/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "sample-udt" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ckb-std = "0.10.0" +perun-common = { path = "../perun-common", default-features = false, features = ["contract"] } diff --git a/payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/entry.rs b/payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/entry.rs new file mode 100644 index 0000000..4e0c757 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/entry.rs @@ -0,0 +1,101 @@ +// Import from `core` instead of from `std` since we are in no-std mode +use core::result::Result; + +// Import CKB syscalls and structures +// https://docs.rs/ckb-std/ +use ckb_std::{ + ckb_constants::Source, + ckb_types::{bytes::Bytes, prelude::*}, + high_level::{load_cell_lock_hash, load_script, load_cell_data}, + syscalls::SysError, +}; +use perun_common::error::Error; + + +pub fn main() -> Result<(), Error> { + let script = load_script()?; + let args: Bytes = script.args().unpack(); + + // return success if owner mode is true + if check_owner_mode(&args)? { + return Ok(()); + } + + let inputs_amount = collect_inputs_amount()?; + let outputs_amount = collect_outputs_amount()?; + + if inputs_amount < outputs_amount { + return Err(Error::DecreasingAmount); + } + + Ok(()) +} + +pub fn check_owner_mode(args: &Bytes) -> Result { + // With owner lock script extracted, we will look through each input in the + // current transaction to see if any unlocked cell uses owner lock. + for i in 0.. { + // check input's lock_hash with script args + let lock_hash = match load_cell_lock_hash( + i, + Source::Input, + ) { + Ok(lock_hash) => lock_hash, + Err(SysError::IndexOutOfBound) => return Ok(false), + Err(err) => return Err(err.into()), + }; + // invalid length of loaded data + if args[..] == lock_hash[..] { + return Ok(true); + } + } + Ok(false) +} + +const UDT_LEN: usize = 16; + +pub fn collect_inputs_amount() -> Result { + // let's loop through all input cells containing current UDTs, + // and gather the sum of all input tokens. + let mut inputs_amount: u128 = 0; + let mut buf = [0u8; UDT_LEN]; + + // u128 is 16 bytes + for i in 0.. { + let data = match load_cell_data(i, Source::GroupInput) { + Ok(data) => data, + Err(SysError::IndexOutOfBound) => break, + Err(err) => return Err(err.into()), + }; + + if data.len() != UDT_LEN { + return Err(Error::Encoding); + } + buf.copy_from_slice(&data); + inputs_amount += u128::from_le_bytes(buf); + } + Ok(inputs_amount) +} + +fn collect_outputs_amount() -> Result { + // With the sum of all input UDT tokens gathered, let's now iterate through + // output cells to grab the sum of all output UDT tokens. + let mut outputs_amount: u128 = 0; + + // u128 is 16 bytes + let mut buf = [0u8; UDT_LEN]; + for i in 0.. { + let data = match load_cell_data(i, Source::GroupOutput) { + Ok(data) => data, + Err(SysError::IndexOutOfBound) => break, + Err(err) => return Err(err.into()), + }; + + if data.len() != UDT_LEN { + return Err(Error::Encoding); + } + buf.copy_from_slice(&data); + outputs_amount += u128::from_le_bytes(buf); + } + Ok(outputs_amount) +} \ No newline at end of file diff --git a/payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/main.rs b/payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/main.rs new file mode 100644 index 0000000..9306fc4 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/contracts/sample-udt/src/main.rs @@ -0,0 +1,32 @@ +//! Generated by capsule +//! +//! `main.rs` is used to define rust lang items and modules. +//! See `entry.rs` for the `main` function. +//! See `error.rs` for the `Error` type. + +#![no_std] +#![no_main] +#![feature(asm_sym)] +#![feature(lang_items)] +#![feature(alloc_error_handler)] +#![feature(panic_info_message)] + +// define modules +mod entry; + +use ckb_std::default_alloc; +use core::arch::asm; + +ckb_std::entry!(program_entry); +default_alloc!(); + +/// program entry +/// +/// Both `argc` and `argv` can be omitted. +fn program_entry(_argc: u64, _argv: *const *const u8) -> i8 { + // Call main function and return error code + match entry::main() { + Ok(_) => 0, + Err(err) => err as i8, + } +} diff --git a/payment-channel-ckb/devnet/contracts/deployment/dev/deployment.toml b/payment-channel-ckb/devnet/contracts/deployment/dev/deployment.toml new file mode 100644 index 0000000..899072d --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/deployment/dev/deployment.toml @@ -0,0 +1,41 @@ +[[cells]] +name = "pcts" +enable_type_id = false +location = { file = "build/release/perun-channel-typescript" } + +[[cells]] +name = "pcls" +enable_type_id = false +location = { file = "build/release/perun-channel-lockscript" } + +[[cells]] +name = "pfls" +enable_type_id = false +location = { file = "build/release/perun-funds-lockscript" } + +[[cells]] +name = "sudt" +enable_type_id = false +location = { file = "build/release/sample-udt" } + +# +# # reference to on-chain cells +# [[cells]] +# name = "genesis_cell" +# enable_type_id = false +# location = { tx_hash = "0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c", index = 0 } + +# # Dep group cells +# [[dep_groups]] +# name = "my_dep_group" +# cells = [ +# "my_cell", +# "genesis_cell" +# ] + +# # Replace with your own lock if you want to unlock deployed cells. +# # For example the secp256k1 lock +[lock] +code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +args = "0x4a3c88737f8d4d26031ad9a6d6448ba75f1533e2" +hash_type = "type" diff --git a/payment-channel-ckb/devnet/contracts/deployment/release/deployment.toml b/payment-channel-ckb/devnet/contracts/deployment/release/deployment.toml new file mode 100644 index 0000000..65968f5 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/deployment/release/deployment.toml @@ -0,0 +1,37 @@ +[[cells]] +name = "pcts" +enable_type_id = false +location = { file = "build/release/perun-channel-typescript" } + +[[cells]] +name = "pcls" +enable_type_id = false +location = { file = "build/release/perun-channel-lockscript" } + +[[cells]] +name = "pfls" +enable_type_id = false +location = { file = "build/release/perun-funds-lockscript" } + +# +# # reference to on-chain cells +# [[cells]] +# name = "genesis_cell" +# enable_type_id = false +# location = { tx_hash = "0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c", index = 0 } + +# # Dep group cells +# [[dep_groups]] +# name = "my_dep_group" +# cells = [ +# "my_cell", +# "genesis_cell" +# ] + +# # Replace with your own lock if you want to unlock deployed cells. +# # For example the secp256k1 lock +# [lock] +# code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +# args = "0x1edcdab5ec7e0f748c60815fce513ee1fe4d63ee" +# hash_type = "type" + diff --git a/payment-channel-ckb/devnet/contracts/migrations/.gitkeep b/payment-channel-ckb/devnet/contracts/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/payment-channel-ckb/devnet/contracts/tests/Cargo.lock b/payment-channel-ckb/devnet/contracts/tests/Cargo.lock new file mode 100644 index 0000000..d7d027c --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/Cargo.lock @@ -0,0 +1,1593 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.9", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "blake2b-ref" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95916998c798756098a4eb1b3f2cd510659705a9817bf203d61abd30fbec3e7b" + +[[package]] +name = "blake2b-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89a8565807f21b913288968e391819e7f9b2f0f46c7b89549c051cccf3a2771" +dependencies = [ + "cc", + "cty", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "buddy-alloc" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3240a4cb09cf0da6a51641bd40ce90e96ea6065e3a1adc46434029254bcc2d09" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ckb-always-success-script" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3b72a38c9920a29990df12002c4d069a147c8782f0c211f8a01b2df8f42bfd" + +[[package]] +name = "ckb-chain-spec" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78df45446aaa86b06a77b8b145cffa79950e7ede293cebcd114a62e74c29dbf" +dependencies = [ + "ckb-constant", + "ckb-crypto", + "ckb-dao-utils", + "ckb-error", + "ckb-hash", + "ckb-jsonrpc-types", + "ckb-pow", + "ckb-rational", + "ckb-resource", + "ckb-traits", + "ckb-types", + "ckb-util", + "serde", + "toml", +] + +[[package]] +name = "ckb-channel" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "920f26cc48cadcaf6f7bcc3960fde9f9f355633b6361da8ef31e1e1c00fc8858" +dependencies = [ + "crossbeam-channel", +] + +[[package]] +name = "ckb-constant" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302566408e5b296663ac5e8245bf71824ca2c7c2ef19a57fcc15939dd66527e9" + +[[package]] +name = "ckb-crypto" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac31177b0a8bf3acd563c042775e40494e437b2bbbae96ac2473eec3a4da95d" +dependencies = [ + "ckb-fixed-hash", + "faster-hex", + "lazy_static", + "rand 0.7.3", + "secp256k1", + "thiserror", +] + +[[package]] +name = "ckb-dao" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b70944b9013ead64287b87ac19608a3ca5ab19a9f29b7a76f637ad7831510e88" +dependencies = [ + "byteorder", + "ckb-chain-spec", + "ckb-dao-utils", + "ckb-traits", + "ckb-types", +] + +[[package]] +name = "ckb-dao-utils" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1929c9627923fe1d22151361d74f5a5aa0dda77016d020307a54486eae11cb3c" +dependencies = [ + "byteorder", + "ckb-error", + "ckb-types", +] + +[[package]] +name = "ckb-error" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446a519d8a847d97f1c8ece739dc1748751a9a2179249c96c45cced0825a7aa5" +dependencies = [ + "anyhow", + "ckb-occupied-capacity", + "derive_more", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00cbbc455b23748b32e06d16628a03e30d56ffa057f17093fdf5b42d4fb6c879" +dependencies = [ + "ckb-fixed-hash-core", + "ckb-fixed-hash-macros", +] + +[[package]] +name = "ckb-fixed-hash-core" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e644a4e026625b4be5a04cdf6c02043080e79feaf77d9cdbb2f0e6553f751" +dependencies = [ + "faster-hex", + "serde", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash-macros" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cfc980ef88c217825172eb46df269f47890f5e78a38214416f13b3bd17a4b4" +dependencies = [ + "ckb-fixed-hash-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-hash" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d9b683e89ae4ffdd5aaf4172eab00b6bbe7ea24e2abf77d3eb850ba36e8983" +dependencies = [ + "blake2b-ref", + "blake2b-rs", +] + +[[package]] +name = "ckb-jsonrpc-types" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac087657eaf964e729f40b3c929d3dac74a2cd8bb38d5e588756e2495711f810" +dependencies = [ + "ckb-types", + "faster-hex", + "serde", + "serde_json", +] + +[[package]] +name = "ckb-logger" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911c4695ddf82f78da8f514b359092bbe231f58c2669c93b1cfc9a2030b125bb" +dependencies = [ + "log", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ckb-occupied-capacity" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2a1dd0d4ba5dafba1e30d437c1148b20f42edb76b6794323e05bda626754eb" +dependencies = [ + "ckb-occupied-capacity-core", + "ckb-occupied-capacity-macros", +] + +[[package]] +name = "ckb-occupied-capacity-core" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ebba3d564098a84c83f4740e1dce48a5e2da759becdb47e3c7965f0808e6e92" +dependencies = [ + "serde", +] + +[[package]] +name = "ckb-occupied-capacity-macros" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6321bba85cdf9724029d8c906851dd4a90906869b42f9100b16645a1261d4c" +dependencies = [ + "ckb-occupied-capacity-core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-pow" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9167b427f42874e68e20e6946d5211709979ff1d86c0061a71c2f6a6aa17659" +dependencies = [ + "byteorder", + "ckb-hash", + "ckb-types", + "eaglesong", + "log", + "serde", +] + +[[package]] +name = "ckb-rational" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2519249f8d47fa758d3fb3cf3049327c69ce0f2acd79d61427482c8661d3dbd" +dependencies = [ + "numext-fixed-uint", + "serde", +] + +[[package]] +name = "ckb-resource" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3abddc968d7f1e70584ab04180c347380a44acbe0b60e26cc96208ec8885279" +dependencies = [ + "ckb-system-scripts", + "ckb-types", + "includedir", + "includedir_codegen", + "phf", + "serde", + "walkdir", +] + +[[package]] +name = "ckb-script" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b4754a2f0ccea5ea1934822bd18a3a66c46344d8c3872cb20ffdcf0851fab9" +dependencies = [ + "byteorder", + "ckb-chain-spec", + "ckb-error", + "ckb-hash", + "ckb-logger", + "ckb-traits", + "ckb-types", + "ckb-vm", + "faster-hex", + "serde", +] + +[[package]] +name = "ckb-standalone-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22d7cbbdab96e6b809a102cf88bfec28795a0a3c06bfdea4abe4de89777801cd" +dependencies = [ + "cfg-if", + "molecule", +] + +[[package]] +name = "ckb-std" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a6ad40455c446ad6fbb303dae24827fc309f43558f59d1f1b863a9de3e9f81" +dependencies = [ + "buddy-alloc", + "cc", + "ckb-standalone-types", + "cstr_core", +] + +[[package]] +name = "ckb-system-scripts" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa5c59063142de7a68cfad4449c6b3863563856219a2925dfb8c5f019ec2aa47" +dependencies = [ + "blake2b-rs", + "faster-hex", + "includedir", + "includedir_codegen", + "phf", +] + +[[package]] +name = "ckb-systemtime" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243197680f69d6bb6cb1caf16199ce4a8162a258c757d5af8f727af0d8aabe9e" + +[[package]] +name = "ckb-testtool" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15fed7e8aeb21e981246bc39e0bd49067f7734851798bef996389a3b02bf9b4e" +dependencies = [ + "ckb-always-success-script", + "ckb-chain-spec", + "ckb-crypto", + "ckb-error", + "ckb-hash", + "ckb-jsonrpc-types", + "ckb-resource", + "ckb-script", + "ckb-traits", + "ckb-types", + "ckb-verification", + "lazy_static", + "rand 0.7.3", +] + +[[package]] +name = "ckb-traits" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9d5827f20a396dfb785398db484fe50de93d76c02e1e32287832604a9dda91" +dependencies = [ + "ckb-types", +] + +[[package]] +name = "ckb-types" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c22b3b1ca8f88a8f48e2f73321c0605281c9c6f1e1c4d651c6138265c22291e" +dependencies = [ + "bit-vec", + "bytes", + "ckb-channel", + "ckb-error", + "ckb-fixed-hash", + "ckb-hash", + "ckb-merkle-mountain-range", + "ckb-occupied-capacity", + "ckb-rational", + "derive_more", + "merkle-cbt", + "molecule", + "numext-fixed-uint", + "once_cell", +] + +[[package]] +name = "ckb-util" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d165c6958601dfbfa4cd00c9263ecfb013b4ccb6d9e1d3187bfa62801abc7d" +dependencies = [ + "linked-hash-map", + "once_cell", + "parking_lot", + "regex", +] + +[[package]] +name = "ckb-verification" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc1745cf02f6d628ac04cf58145b853a359ad4d74fdb418207e99773185ad11" +dependencies = [ + "ckb-chain-spec", + "ckb-dao", + "ckb-dao-utils", + "ckb-error", + "ckb-pow", + "ckb-script", + "ckb-systemtime", + "ckb-traits", + "ckb-types", + "ckb-verification-traits", + "derive_more", + "lru", +] + +[[package]] +name = "ckb-verification-traits" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88de577410c2e72ccd18e00cb63fc0000d41be50604a895946a1566a02272730" +dependencies = [ + "bitflags", + "ckb-error", +] + +[[package]] +name = "ckb-vm" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1223acc8054ce96f91c5d99d4942898d0bdadd618c3b14f1acd3e67212991d8e" +dependencies = [ + "byteorder", + "bytes", + "cc", + "ckb-vm-definitions", + "derive_more", + "goblin 0.2.3", + "goblin 0.4.0", + "rand 0.7.3", + "scroll", + "serde", +] + +[[package]] +name = "ckb-vm-definitions" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4af800ae2b6c54b70efa398dab015a09a52eeac2dd1ac3ad32c9bbe224974225" + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cstr_core" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd98742e4fdca832d40cab219dc2e3048de17d873248f83f17df47c1bea70956" +dependencies = [ + "cty", + "memchr", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "eaglesong" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d978bd5d343e8ab9b5c0fc8d93ff9c602fdc96616ffff9c05ac7a155419b824" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest", + "ff", + "generic-array", + "group", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "faster-hex" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "goblin" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "goblin" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532a09cd3df2c6bbfc795fb0434bff8f22255d1d07328180e918a2e6ce122d4d" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heapsize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +dependencies = [ + "winapi", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "includedir" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd126bd778c00c43a9dc76d1609a0894bf4222088088b2217ccc0ce9e816db7" +dependencies = [ + "flate2", + "phf", +] + +[[package]] +name = "includedir_codegen" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac1500c9780957c9808c4ec3b94002f35aab01483833f5a8bce7dfb243e3148" +dependencies = [ + "flate2", + "phf_codegen", + "walkdir", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", + "sha3", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +dependencies = [ + "serde", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "merkle-cbt" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171d2f700835121c3b04ccf0880882987a050fd5c7ae88148abf537d33dd3a56" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "molecule" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc8276c02a006bddad7d1c28c1a88f30421e1b5f0ba0ca96ceb8077c7d20c01" +dependencies = [ + "bytes", + "cfg-if", + "faster-hex", +] + +[[package]] +name = "numext-constructor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "621fe0f044729f810c6815cdd77e8f5e0cd803ce4f6a38380ebfc1322af98661" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "numext-fixed-uint" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c68c76f96d589d1009a666c5072f37f3114d682696505f2cf445f27766c7d70" +dependencies = [ + "numext-fixed-uint-core", + "numext-fixed-uint-hack", +] + +[[package]] +name = "numext-fixed-uint-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aab1d6457b97b49482f22a92f0f58a2f39bdd7f3b2f977eae67e8bc206aa980" +dependencies = [ + "heapsize", + "numext-constructor", + "rand 0.7.3", + "serde", + "thiserror", +] + +[[package]] +name = "numext-fixed-uint-hack" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200f8d55c36ec1b6a8cf810115be85d4814f045e0097dfd50033ba25adb4c9e" +dependencies = [ + "numext-fixed-uint-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "perun-common" +version = "0.1.0" +dependencies = [ + "blake2b-rs", + "ckb-occupied-capacity", + "ckb-std", + "ckb-types", + "k256", + "molecule", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared", + "rand 0.7.3", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.9", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scroll" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tests" +version = "0.1.0" +dependencies = [ + "ckb-occupied-capacity", + "ckb-standalone-types", + "ckb-std", + "ckb-testtool", + "hex", + "k256", + "molecule", + "perun-common", + "rand 0.8.5", + "rand_core 0.6.4", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/payment-channel-ckb/devnet/contracts/tests/Cargo.toml b/payment-channel-ckb/devnet/contracts/tests/Cargo.toml new file mode 100644 index 0000000..7d3dbcb --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tests" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ckb-testtool = "0.9" +hex = "0.4.3" +rand = "0.8.5" +perun-common = { path = "../contracts/perun-common", default-features = false, features = ["testing"] } +molecule = "0.7.3" +ckb-types = { package = "ckb-standalone-types", version = "0.1.2" } +k256 = { version = "0.11.6", default-features = false, features = ["ecdsa", "arithmetic"]} +rand_core = { version = "0.6", features = ["getrandom"] } +ckb-std = "0.10.0" +ckb-occupied-capacity = "0.108.0" diff --git a/payment-channel-ckb/devnet/contracts/tests/src/lib.rs b/payment-channel-ckb/devnet/contracts/tests/src/lib.rs new file mode 100644 index 0000000..c58a205 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/lib.rs @@ -0,0 +1,63 @@ +use ckb_testtool::ckb_types::bytes::Bytes; +use std::env; +use std::fs; +use std::path::PathBuf; +use std::str::FromStr; + +#[cfg(test)] +mod perun; +#[cfg(test)] +mod tests; + +const TEST_ENV_VAR: &str = "CAPSULE_TEST_ENV"; + +pub enum TestEnv { + Debug, + Release, +} + +impl FromStr for TestEnv { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "debug" => Ok(TestEnv::Debug), + "release" => Ok(TestEnv::Release), + _ => Err("no match"), + } + } +} + +pub struct Loader(PathBuf); + +impl Default for Loader { + fn default() -> Self { + let test_env = match env::var(TEST_ENV_VAR) { + Ok(val) => val.parse().expect("test env"), + Err(_) => TestEnv::Debug, + }; + Self::with_test_env(test_env) + } +} + +impl Loader { + fn with_test_env(env: TestEnv) -> Self { + let load_prefix = match env { + TestEnv::Debug => "debug", + TestEnv::Release => "release", + }; + let dir = env::current_dir().unwrap(); + let mut base_path = PathBuf::new(); + base_path.push(dir); + base_path.push(".."); + base_path.push("build"); + base_path.push(load_prefix); + Loader(base_path) + } + + pub fn load_binary(&self, name: &str) -> Bytes { + let mut path = self.0.clone(); + path.push(name); + fs::read(path).expect("binary").into() + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/account.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/account.rs new file mode 100644 index 0000000..6f06ec9 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/account.rs @@ -0,0 +1,41 @@ +use k256::{ecdsa::SigningKey, PublicKey}; +use rand_core::OsRng; +use std::fmt::Debug; + +pub trait Account: Debug + Clone { + fn public_key(&self) -> PublicKey; + fn name(&self) -> String; +} + +#[derive(Clone, Debug)] +pub struct TestAccount { + pub sk: SigningKey, + pub name: String, +} + +impl TestAccount { + pub fn new(sk: SigningKey, name: String) -> Self { + Self { sk, name } + } + + pub fn new_with_random_key(name: String) -> Self { + Self { + sk: SigningKey::random(&mut OsRng), + name, + } + } + + pub fn id(&self) -> &str { + &self.name + } +} + +impl Account for TestAccount { + fn public_key(&self) -> PublicKey { + PublicKey::from(self.sk.verifying_key()) + } + + fn name(&self) -> String { + self.name.clone() + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/action.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/action.rs new file mode 100644 index 0000000..71ea179 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/action.rs @@ -0,0 +1,22 @@ +/// Action is a generic channel action, that occurred in the channel. It is +/// parameterized by the type of the channel state. +pub enum Action +where + S: Applyable, +{ + Open(S), + Fund(S), + Abort(S), + Send(S), + Close(S), + ForceClose(S), +} + +/// Applyable allows to apply an action containing the same state type to its +/// current state. +pub trait Applyable +where + Self: Clone, +{ + fn apply(self, action: &Action) -> Self; +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/channel.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/channel.rs new file mode 100644 index 0000000..266d0da --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/channel.rs @@ -0,0 +1,374 @@ +use ckb_testtool::{ + ckb_types::{ + packed::{Header, OutPoint, RawHeader, Script}, + prelude::{Builder, Entity, Pack, Unpack}, + }, + context::Context, +}; +use k256::ecdsa::VerifyingKey; +use perun_common::{ + ctrue, + perun_types::{ChannelConstants, ChannelStatus, ChannelState}, +}; + +use crate::perun::{ + self, + test::{keys, Client}, +}; +use crate::perun::{harness, test}; +use std::cmp::PartialEq; +use std::collections::HashMap; +use std::fmt::Debug; + +use super::{test::cell::FundingCell, Account}; + +enum ActionValidity { + Valid, + Invalid, +} + +/// Channel is a Perun test channel. It handles the state of said channel +/// together with the participants, the current time and surrounding chain +/// context. +pub struct Channel<'a, S> +where + S: perun::Applyable + Debug + PartialEq, +{ + /// The active party. Actions called on the channel will be issued by this + /// party henceforth. + active_part: test::Client, + /// The id of the channel. + id: test::ChannelId, + /// The cell which represents this channel on-chain. + channel_cell: Option, + /// The current state of this channel. + channel_state: ChannelStatus, + /// The cells locking funds for this channel. + funding_cells: Vec, + /// The used Perun Channel Type Script. + pcts: Script, + /// All available parties. + parts: HashMap, + /// The surrounding chain context. + ctx: &'a mut Context, + /// The intial test harness environment supplying all Perun specific + /// contracts and functionality for deployment etc. + env: &'a harness::Env, + /// The current channel time. + current_time: u64, + /// The validity of the next action. + validity: ActionValidity, + /// The history of actions performed on this channel. + history: Vec>, + /// The currently tracked channel state as produced by the unit under test. + current_state: S, +} + +/// call_action! is a macro that calls the given action on the currently active +/// participant. It also sets the validity of the next action to `Valid`. +macro_rules! call_action { + ($self:ident, $action:ident $(, $x:expr)*$(,)*) => ( + { + println!("calling action {} on {}", stringify!($action), $self.active_part.name()); + let res = match $self.validity { + ActionValidity::Valid => $self.active_part.$action($self.ctx, $self.env, $($x),*), + ActionValidity::Invalid => { + let res = $self.active_part.$action($self.ctx, $self.env, $($x),*); + match res { + Ok(_) => Err(perun::Error::new("action should have failed")), + Err(_) => Ok(Default::default()), + } + } + }; + $self.validity = ActionValidity::Valid; + res + } +) +} + +impl<'a, S> Channel<'a, S> +where + S: Default + perun::Applyable + Debug + PartialEq, +{ + pub fn new( + context: &'a mut Context, + env: &'a perun::harness::Env, + parts: &[perun::TestAccount], + ) -> Self { + let m_parts: HashMap<_, _> = parts + .iter() + .enumerate() + .map(|(i, p)| { + ( + p.name().clone(), + perun::test::Client::new(i as u8, p.name(), p.sk.clone()), + ) + }) + .collect(); + let active = m_parts.get(&parts[0].name()).expect("part not found"); + + Channel { + id: test::ChannelId::new(), + current_time: 0, + ctx: context, + env, + pcts: Script::default(), + channel_cell: None, + channel_state: ChannelStatus::default(), + funding_cells: Vec::new(), + active_part: active.clone(), + parts: m_parts.clone(), + validity: ActionValidity::Valid, + history: Vec::new(), + current_state: S::default(), + } + } + + /// with sets the currently active participant to the given `part`. + pub fn with(&mut self, part: &str) -> &mut Self { + self.active_part = self.parts.get(part).expect("part not found").clone(); + self + } + + /// delay the environment by the given `duration`, this makes the next + /// transaction receive a block_header with a timestamp that is `duration` + /// in the future. + pub fn delay(&mut self, duration: u64) { + self.current_time += duration; + } + + /// open a channel using the currently active participant set by `with(..)` + /// with the value given in `funding_agreement`. + pub fn open(&mut self, funding_agreement: &test::FundingAgreement) -> Result<(), perun::Error> { + let (id, or) = call_action!(self, open, funding_agreement)?; + self.id = id; + self.channel_cell = Some(or.channel_cell.clone()); + // Make sure the channel cell is linked to a header with a timestamp. + self.push_header_with_cell(or.channel_cell); + let mut fs = self.funding_cells.clone(); + fs.extend(or.funds_cells.iter().cloned()); + self.funding_cells = fs.to_vec(); + self.pcts = or.pcts; + self.channel_state = or.state; + Ok(()) + } + + fn push_header_with_cell(&mut self, cell: OutPoint) { + let header = Header::new_builder() + .raw( + RawHeader::new_builder() + .timestamp(self.current_time.pack()) + .build(), + ) + .build() + .into_view(); + self.ctx.insert_header(header.clone()); + // We will always use 0 as the `tx_index`. + self.ctx.link_cell_with_block(cell, header.hash(), 0); + } + + /// fund a channel using the currently active participant set by `with(..)` + /// with the value given in `funding_agreement`. + pub fn fund(&mut self, funding_agreement: &test::FundingAgreement) -> Result<(), perun::Error> { + // TODO: Lift this check into the type-system to make this more readable and stick to DRY. + let res = match &self.channel_cell { + Some(channel_cell) => { + call_action!( + self, + fund, + self.id, + funding_agreement, + channel_cell.clone(), + self.channel_state.clone(), + self.pcts.clone() + ) + } + None => panic!("no channel cell, invalid test setup"), + }?; + // TODO: DRY please. + self.channel_state = res.state; + self.channel_cell = Some(res.channel_cell.clone()); + self.push_header_with_cell(res.channel_cell); + let mut fs = self.funding_cells.clone(); + fs.extend(res.funds_cells.iter().cloned()); + self.funding_cells = fs.to_vec(); + Ok(()) + } + + /// send a payment using the currently active participant set by `with(..)` + /// to the given `to` participant. + pub fn send(&mut self, to: &P, amount: u64) -> Result<(), perun::Error> { + let to = self.parts.get(&to.name()).expect("part not found"); + self.active_part.send(self.ctx, self.env) + } + + /// dispute a channel using the currently active participant set by + /// `with(..)`. + pub fn dispute(&mut self) -> Result<(), perun::Error> { + self.channel_state = self.channel_state.clone().as_builder().disputed(ctrue!()).build(); + let sigs = self.sigs_for_channel_state()?; + let res = match &self.channel_cell { + Some(channel_cell) => { + call_action!( + self, + dispute, + self.id, + channel_cell.clone(), + self.channel_state.clone(), + self.pcts.clone(), + sigs, + ) + } + None => panic!("no channel cell, invalid test setup"), + }?; + self.channel_cell = Some(res.channel_cell.clone()); + self.push_header_with_cell(res.channel_cell); + Ok(()) + } + + /// abort a channel using the currently active participant set by + /// `with(..)`. + pub fn abort(&mut self) -> Result<(), perun::Error> { + match &self.channel_cell { + Some(channel_cell) => { + call_action!( + self, + abort, + self.id, + self.channel_state.clone(), + channel_cell.clone(), + self.funding_cells.clone() + ) + } + None => panic!("no channel cell, invalid test setup"), + }?; + Ok(()) + } + + /// finalize finalizes the channel state in use. It has to be called for + /// before successful close actions. It bumps the version of the channel state. + pub fn finalize(&mut self) -> &mut Self { + let status = self.channel_state.clone(); + let old_version: u64 = status.state().version().unpack(); + let state = status.state().as_builder().is_final(ctrue!()).version((old_version + 1).pack()).build(); + self.channel_state = status.as_builder().state(state).build(); + self + } + + pub fn update(&mut self, update: impl Fn(&ChannelState) -> Result) -> &mut Self { + let new_state = update(&self.channel_state.state()).expect("update failed"); + self.channel_state = self.channel_state + .clone() + .as_builder() + .state(new_state) + .build(); + self + } + + /// close a channel using the currently active participant set by + /// `with(..)`. + pub fn close(&mut self) -> Result<(), perun::Error> { + let sigs = self.sigs_for_channel_state()?; + match self.channel_cell.clone() { + Some(channel_cell) => call_action!( + self, + close, + self.id, + channel_cell, + self.funding_cells.clone(), + self.channel_state.clone(), + sigs + ), + None => panic!("no channel cell, invalid test setup"), + }?; + Ok(()) + } + + fn sigs_for_channel_state(&self) -> Result<[Vec; 2], perun::Error> { + // We have to unpack the ChannelConstants like this. Otherwise the molecule header is still + // part of the slice. On-chain we have no problem due to unpacking the arguments, but this + // does not seem possible in this scope. + let bytes = self.pcts.args().raw_data(); + // We want to have the correct order of clients in an array to construct signatures. For + // consistency we use the ChannelConstants which are also used to construct the channel and + // look up the participants according to their public key identifier. + let s = ChannelConstants::from_slice(&bytes)?; + let resolve_client = |verifying_key_raw: Vec| -> Result { + let verifying_key = VerifyingKey::from_sec1_bytes(verifying_key_raw.as_slice())?; + let pubkey = keys::verifying_key_to_byte_array(&verifying_key); + self.parts + .values() + .cloned() + .find(|c| c.pubkey() == pubkey) + .ok_or("unknown participant in channel parameters".into()) + }; + let clients: Result, _> = s + .params() + .mk_party_pubkeys() + .iter() + .cloned() + .map(resolve_client) + .collect(); + let sigs: Result, _> = clients? + .iter() + .map(|p| p.sign(self.channel_state.state())) + .collect(); + let sig_arr: [Vec; 2] = sigs?.try_into()?; + Ok(sig_arr) + } + + /// force_close a channel using the currently active participant set by + /// `with(..)`. + pub fn force_close(&mut self) -> Result<(), perun::Error> { + let h = Header::new_builder() + .raw( + RawHeader::new_builder() + .timestamp(self.current_time.pack()) + .build(), + ) + .build() + .into_view(); + // Push a header with the current time which can be used in force_close + // as for time validation purposes. + self.ctx.insert_header(h.clone()); + match self.channel_cell.clone() { + Some(channel_cell) => call_action!( + self, + force_close, + self.id, + channel_cell, + self.funding_cells.clone(), + self.channel_state.clone(), + ), + None => panic!("no channel cell, invalid test setup"), + }?; + Ok(()) + } + + /// valid sets the validity of the next action to valid. (default) + pub fn valid(&mut self) -> &mut Self { + self.validity = ActionValidity::Valid; + self + } + + /// invalid sets the validity of the next action to invalid. It resets to + /// valid after the next action. + pub fn invalid(&mut self) -> &mut Self { + self.validity = ActionValidity::Invalid; + self + } + + /// assert asserts that the channel is in a valid state according to all + /// actions that have been performed on it. This also includes the + /// surrounding context for this channel. + /// + /// If a channel was closed, it will also assert that all participants + /// were properly paid. + pub fn assert(&self) { + let expected_state: S = self + .history + .iter() + .fold(Default::default(), |acc, act| acc.apply(act)); + assert_eq!(expected_state, self.current_state) + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/error.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/error.rs new file mode 100644 index 0000000..6af554f --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/error.rs @@ -0,0 +1,65 @@ +use molecule::error::VerificationError; +use std::{error, fmt}; + +use ckb_testtool::ckb_error; + +#[derive(Debug)] +pub struct Error { + details: String, +} + +impl Error { + pub fn new(msg: &str) -> Error { + Error { + details: msg.to_string(), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.details) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + &self.details + } +} + +impl From<&str> for Error { + fn from(err: &str) -> Error { + Error::new(err) + } +} + +impl From for Error { + fn from(err: ckb_occupied_capacity::Error) -> Error { + Error::new(&err.to_string()) + } +} + +impl From for Error { + fn from(err: ckb_error::Error) -> Error { + Error::new(&err.to_string()) + } +} + +impl From for Error { + fn from(err: k256::ecdsa::Error) -> Error { + Error::new(&err.to_string()) + } +} + +impl From for Error { + fn from(err: VerificationError) -> Error { + Error::new(&err.to_string()) + } +} + +impl From>> for Error { + fn from(vs: Vec>) -> Error { + Error::new(&format!("converting from nested vectors: {:?}", vs)) + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/harness.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/harness.rs new file mode 100644 index 0000000..4e288fc --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/harness.rs @@ -0,0 +1,285 @@ +use crate::perun; +use crate::Loader; +use ckb_occupied_capacity::{Capacity, IntoCapacity}; +use ckb_testtool::{ + builtin::ALWAYS_SUCCESS, + ckb_types::{bytes::Bytes, packed::*, prelude::*}, + context::Context, +}; +use perun_common::cfalse; +use perun_common::perun_types::ChannelStateBuilder; +use perun_common::perun_types::ChannelStatusBuilder; +use perun_common::perun_types::{self, ChannelStatus, ChannelToken}; + +use super::test::ChannelId; +use super::test::FundingAgreement; +use super::test::FundingAgreementEntry; + +// Env contains all chain information required for running Perun +// tests. +pub struct Env { + // Perun contracts. + pub pcls_out_point: OutPoint, + pub pcts_out_point: OutPoint, + pub pfls_out_point: OutPoint, + // Auxiliary contracts. + pub always_success_out_point: OutPoint, + pub sample_udt_out_point: OutPoint, + + // Perun scripts. + pcls_script: Script, + pcts_script: Script, + pfls_script: Script, + pub pcls_script_dep: CellDep, + pub pcts_script_dep: CellDep, + pub pfls_script_dep: CellDep, + // Auxiliary scripts. + pub always_success_script: Script, + pub always_success_script_dep: CellDep, + pub sample_udt_script: Script, + pub sample_udt_script_dep: CellDep, + // Maximum amount of cycles used when verifying TXs. + pub max_cycles: u64, + pub min_capacity_no_script: Capacity, + pub min_capacity_pfls: Capacity, + pub sample_udt_max_cap: Capacity, + pub challenge_duration: u64, +} + +impl Env { + // prepare_env prepares the given context to be used for running Perun + // tests. + pub fn new( + context: &mut Context, + max_cycles: u64, + challenge_duration: u64, + ) -> Result { + // Perun contracts. + let pcls: Bytes = Loader::default().load_binary("perun-channel-lockscript"); + let pcts: Bytes = Loader::default().load_binary("perun-channel-typescript"); + let pfls: Bytes = Loader::default().load_binary("perun-funds-lockscript"); + let sample_udt: Bytes = Loader::default().load_binary("sample-udt"); + // Deploying the contracts returns the cell they are deployed in. + let pcls_out_point = context.deploy_cell(pcls); + let pcts_out_point = context.deploy_cell(pcts); + let pfls_out_point = context.deploy_cell(pfls); + let sample_udt_out_point = context.deploy_cell(sample_udt); + // Auxiliary contracts. + let always_success_out_point = context.deploy_cell(ALWAYS_SUCCESS.clone()); + + // Prepare scripts. + // Perun scripts. + let pcls_script = context + .build_script(&pcls_out_point, Default::default()) + .ok_or("perun-channel-lockscript")?; + let pcts_script = context + .build_script( + &pcts_out_point, + perun_types::ChannelConstants::default().as_bytes(), + ) + .ok_or("perun-channel-typescript")?; + let pfls_script = context + .build_script(&pfls_out_point, Default::default()) + .ok_or("perun-funds-lockscript")?; + let sample_udt_script = context + .build_script(&sample_udt_out_point, Default::default()) + .ok_or("sample-udt")?; + let pcls_script_dep = CellDep::new_builder() + .out_point(pcls_out_point.clone()) + .build(); + let pcts_script_dep = CellDep::new_builder() + .out_point(pcts_out_point.clone()) + .build(); + let pfls_script_dep = CellDep::new_builder() + .out_point(pfls_out_point.clone()) + .build(); + let sample_udt_script_dep = CellDep::new_builder() + .out_point(sample_udt_out_point.clone()) + .build(); + let sample_udt_max_cap = sample_udt_script.occupied_capacity()?.safe_mul(Capacity::shannons(10))?; + // Auxiliary scripts. + let always_success_script = context + .build_script(&always_success_out_point, Bytes::from(vec![0])) + .expect("always_success"); + let always_success_script_dep = CellDep::new_builder() + .out_point(always_success_out_point.clone()) + .build(); + + // Calculate minimum amount of capacity required for a cell using the always success script. + let tmp_output = CellOutput::new_builder() + .capacity(0u64.pack()) + .lock(always_success_script.clone()) + .build(); + let min_capacity_no_script = tmp_output.occupied_capacity(0u64.into_capacity())?; + + // Calculate minimum amount of capacity required for a cell using the PFLS script. + let tmp_output = CellOutput::new_builder() + .capacity(0u64.pack()) + .lock(pfls_script.clone()) + .build(); + let pfls_args_capacity = pcts_script.calc_script_hash().as_bytes().len() as u64; + let min_capacity_pfls = tmp_output.occupied_capacity(pfls_args_capacity.into_capacity())?; + println!("pfls code hash: {}", pfls_script.code_hash()); + println!("asset code hash: {}", sample_udt_script.code_hash()); + println!("pcts code hash: {}", pcts_script.code_hash()); + println!("pcls code hash: {}", pcls_script.code_hash()); + println!("always_success code hash: {}", always_success_script.code_hash()); + Ok(Env { + pcls_out_point, + pcts_out_point, + pfls_out_point, + always_success_out_point, + sample_udt_out_point, + pcls_script, + pcts_script, + pfls_script, + pcls_script_dep, + pcts_script_dep, + pfls_script_dep, + always_success_script, + always_success_script_dep, + sample_udt_script, + sample_udt_script_dep, + max_cycles, + min_capacity_no_script, + min_capacity_pfls, + sample_udt_max_cap, + challenge_duration, + }) + } + + pub fn build_pcls(&self, context: &mut Context, args: Bytes) -> Script { + let pcls_out_point = &self.pcls_out_point; + context + .build_script(pcls_out_point, args) + .expect("perun-channel-lockscript") + } + + pub fn build_pcts(&self, context: &mut Context, args: Bytes) -> Script { + let pcts_out_point = &self.pcts_out_point; + context + .build_script(pcts_out_point, args) + .expect("perun-channel-typescript") + } + + pub fn build_pfls(&self, context: &mut Context, args: Bytes) -> Script { + let pfls_out_point = &self.pfls_out_point; + context + .build_script(pfls_out_point, args) + .expect("perun-funds-lockscript") + } + + pub fn build_lock_script(&self, context: &mut Context, args: Bytes) -> Script { + let always_success_out_point = &self.always_success_out_point; + context + .build_script(always_success_out_point, args) + .expect("always_success") + } + + pub fn min_capacity_for_channel(&self, cs: ChannelStatus) -> Result { + let tmp_output = CellOutput::new_builder() + .capacity(0u64.pack()) + .lock(self.pcls_script.clone()) + .type_(Some(self.pcts_script.clone()).pack()) + .build(); + let cs_capacity = Capacity::bytes(cs.as_bytes().len())?; + let min_capacity = tmp_output.occupied_capacity(cs_capacity)?; + Ok(min_capacity) + } + + pub fn create_channel_token(&self, context: &mut Context) -> (ChannelToken, OutPoint) { + let channel_token_outpoint = context.create_cell( + CellOutput::new_builder() + .capacity(self.min_capacity_no_script.pack()) + .lock(self.always_success_script.clone()) + .build(), + Bytes::default(), + ); + let packed_outpoint = OutPointBuilder::default() + .tx_hash(channel_token_outpoint.tx_hash()) + .index(channel_token_outpoint.index()) + .build(); + ( + perun_types::ChannelTokenBuilder::default() + .out_point(packed_outpoint.clone()) + .build(), + packed_outpoint, + ) + } + + /// create_funds_from_agreement creates a new cell with the funds for the given party index locked + /// by the always_success_script parameterized on the party index. + pub fn create_funds_from_agreement( + &self, + context: &mut Context, + party_index: u8, + funding_agreement: &FundingAgreement, + ) -> Result, perun::Error> { + let mut funds = self.create_ckbytes_funds_for_index(context, party_index, funding_agreement.expected_ckbytes_funding_for(party_index)?)?; + funds.append(self.create_sudts_funds_for_index(context, party_index, funding_agreement.expected_sudts_funding_for(party_index)?)?.as_mut()); + return Ok(funds); + } + + pub fn create_ckbytes_funds_for_index( + &self, + context: &mut Context, + party_index: u8, + required_funds: u64, + ) -> Result, perun::Error> { + // Create cell containing the required funds for this party. + let my_output = CellOutput::new_builder() + .capacity(required_funds.pack()) + // Lock cell using the correct party index. + .lock(self.build_lock_script(context, Bytes::from(vec![party_index]))) + .build(); + let cell = context.create_cell(my_output.clone(), Bytes::default()); + Ok(vec![(cell, required_funds.into_capacity())]) + } + + pub fn create_sudts_funds_for_index(&self, context: &mut Context, party_index: u8, required_funds: Vec<(Script, Capacity, u128)>) -> Result, perun::Error> { + let mut outs: Vec<(OutPoint, Capacity)> = Vec::new(); + for (sudt_script, capacity, amount) in required_funds { + let my_output = CellOutput::new_builder() + .capacity(capacity.pack()) + // Lock cell using the correct party index. + .lock(self.build_lock_script(context, Bytes::from(vec![party_index]))) + .type_(Some(sudt_script).pack()) + .build(); + let cell = context.create_cell(my_output.clone(), Bytes::from(amount.to_le_bytes().to_vec())); + outs.push((cell, capacity)); + } + Ok(outs) + } + + pub fn create_min_cell_for_index(&self, context: &mut Context, party_index: u8) -> OutPoint { + self.create_ckbytes_funds_for_index(context, party_index, self.min_capacity_no_script.as_u64()) + .unwrap() + .get(0).unwrap().clone().0 + } + + pub fn build_initial_channel_state( + &self, + channel_id: ChannelId, + client_index: u8, + funding_agreement: &FundingAgreement, + ) -> Result { + let all_indices = funding_agreement + .content() + .iter() + .map(|FundingAgreementEntry { index, .. }| *index) + .collect::>(); + let channel_balances = funding_agreement.mk_balances(all_indices)?; + let channel_state = ChannelStateBuilder::default() + .channel_id(channel_id.to_byte32()) + .balances(channel_balances) + .version(Default::default()) + .is_final(cfalse!()) + .build(); + let channel_status = ChannelStatusBuilder::default() + .state(channel_state) + .funded(cfalse!()) + .disputed(cfalse!()) + .build(); + Ok(channel_status) + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/mod.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/mod.rs new file mode 100644 index 0000000..cbf1768 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/mod.rs @@ -0,0 +1,22 @@ +#[cfg(test)] +pub mod harness; + +mod error; +pub use error::*; + +pub mod channel; + +pub mod mutators; + +pub mod test; + +mod action; +pub use action::*; + +mod state; +pub use state::*; + +pub mod random; + +mod account; +pub use account::*; diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/mutators.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/mutators.rs new file mode 100644 index 0000000..24abb8c --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/mutators.rs @@ -0,0 +1,69 @@ +use crate::perun; +use ckb_testtool::ckb_types::prelude::{Unpack, Pack}; +use molecule::prelude::{Entity, Builder}; +use perun_common::perun_types::{ChannelState, CKByteDistribution, SUDTDistribution}; + +pub enum Direction { + AtoB, + BtoA, +} + +/// id returns a mutator that does not change the channel state. +pub fn id() -> impl Fn(&ChannelState) -> Result { + |s| Ok(s.clone()) +} + +/// bump_version returns a mutator that bumps the version number of the channel state. +pub fn bump_version() -> impl Fn(&ChannelState) -> Result { + |s| Ok(s.clone().as_builder().version((Unpack::::unpack(&s.version()) + 1u64).pack()).build()) +} + +/// pay_ckbytes returns a mutator that transfers the given amount of CKBytes from one party to the other according to the +/// specified direction. It also bumps the version number of the channel state. +pub fn pay_ckbytes(direction: Direction, amount: u64) -> impl Fn(&ChannelState) -> Result { + let (sender_index, receiver_index) = get_indices(direction); + move |s| { + let s_bumped = bump_version()(s)?; + let mut distribution = s_bumped.balances().ckbytes().to_array(); + if distribution[sender_index] < amount { + return Err(perun::Error::new("insufficient funds")); + } + distribution[sender_index] -= amount; + distribution[receiver_index] += amount; + let balances = s_bumped.balances().clone().as_builder().ckbytes(CKByteDistribution::from_array(distribution)).build(); + Ok(s_bumped.clone().as_builder().balances(balances).build()) + } +} + +/// pay_sudt returns a mutator that transfers the given amount of the specified SUDT index from one party to the other according to the +/// specified direction. It also bumps the version number of the channel state. +pub fn pay_sudt(direction:Direction, amount: u128, asset_index: usize)-> impl Fn(&ChannelState) -> Result { + let (sender_index, receiver_index) = get_indices(direction); + move |s| { + let s_bumped = bump_version()(s)?; + let sudts = s_bumped.balances().sudts().clone(); + if asset_index >= sudts.len() { + return Err(perun::Error::new("asset index out of bounds")); + } + let sudt = sudts.get(asset_index).unwrap(); + let mut distribution = sudt.distribution().to_array(); + if distribution[sender_index] < amount { + return Err(perun::Error::new("insufficient funds")); + } + distribution[sender_index] -= amount; + distribution[receiver_index] += amount; + let packed_sudt = sudt.clone().as_builder().distribution(SUDTDistribution::from_array(distribution)).build(); + let mut new_sudts = sudts.clone().as_builder(); + new_sudts.replace(asset_index, packed_sudt).unwrap(); + let balances = s_bumped.balances().clone().as_builder().sudts(new_sudts.build()).build(); + Ok(s_bumped.clone().as_builder().balances(balances).build()) + } +} + +/// get_indices returns (sender_index, receiver_index) +fn get_indices(direction: Direction) -> (usize, usize) { + match direction { + Direction::AtoB => (0, 1), + Direction::BtoA => (1, 0), + } +} \ No newline at end of file diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/random.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/random.rs new file mode 100644 index 0000000..3351e08 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/random.rs @@ -0,0 +1,13 @@ +use rand::Rng; + +use super::TestAccount; + +pub fn nonce() -> [u8; 32] { + let mut rng = rand::thread_rng(); + let nonce: [u8; 32] = rng.gen(); + nonce +} + +pub fn account(name: &str) -> TestAccount { + TestAccount::new_with_random_key(name.to_string()) +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/state.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/state.rs new file mode 100644 index 0000000..a06a364 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/state.rs @@ -0,0 +1,17 @@ +use crate::perun::{Action, Applyable}; + +#[derive(Debug, Clone, Default, PartialEq)] +pub struct State {} + +impl Applyable for State { + fn apply(self, action: &Action) -> Self { + match action { + Action::Open(_) => self, + Action::Fund(_) => self, + Action::Abort(_) => self, + Action::Send(_) => self, + Action::Close(_) => self, + Action::ForceClose(_) => self, + } + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/cell.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/cell.rs new file mode 100644 index 0000000..668d262 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/cell.rs @@ -0,0 +1,74 @@ +use ckb_testtool::{ckb_types::{packed::{OutPoint, CellOutput}, prelude::{Unpack, Pack}}}; +use ckb_types::bytes; +use molecule::prelude::{Entity, Builder}; + +use super::{Asset, AssetRegister}; + + +#[derive(Debug, Clone)] + +pub enum FundingCell { + FundingCellCKBytes(FundingCellCKBytes), + FundingCellSUDT(FundingCellSUDT), +} + +#[derive(Debug, Clone)] +pub struct FundingCellCKBytes { + // Index of the party whose initial funds are contained in this cell. + pub index: u8, + // The amount of funding for the party given by index. + pub cap: u64, + // The outpoint of the cell containing the funds. + pub out_point: OutPoint, +} + +#[derive(Debug, Clone)] +pub struct FundingCellSUDT { + // Index of the party whose initial funds are contained in this cell. + pub index: u8, + // The amount of funding for the party given by index. + pub cap: u64, + // The outpoint of the cell containing the funds. + pub out_point: OutPoint, + pub asset: Asset, + pub asset_amount: u128, +} + +impl Default for FundingCell { + fn default() -> Self { + FundingCell::FundingCellCKBytes(FundingCellCKBytes { + index: 0, + cap: 0, + out_point: OutPoint::default(), + }) + } +} + +pub fn mk_funding_cell(party_index: u8, out_point: OutPoint, cell_output: &CellOutput, data: bytes::Bytes, register: &AssetRegister) -> FundingCell { + if cell_output.type_().is_some(){ + let asset = register.guess_asset_from_script(&cell_output.type_().to_opt().unwrap()).unwrap(); + FundingCell::FundingCellSUDT(FundingCellSUDT { + index: party_index, + cap: cell_output.capacity().unpack(), + out_point, + asset: asset.clone(), + asset_amount: u128::from_le_bytes(data.to_vec().as_slice().try_into().unwrap()), + }) + } else { + FundingCell::FundingCellCKBytes(FundingCellCKBytes { + index: party_index, + cap: cell_output.capacity().unpack(), + out_point, + }) + } + +} + +impl FundingCell { + pub fn outpoint(&self) -> OutPoint { + match self { + FundingCell::FundingCellCKBytes(f) => f.out_point.clone(), + FundingCell::FundingCellSUDT(f) => f.out_point.clone(), + } + } +} \ No newline at end of file diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/channel_id.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/channel_id.rs new file mode 100644 index 0000000..3b0f445 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/channel_id.rs @@ -0,0 +1,38 @@ +use ckb_testtool::ckb_types::{ + packed::{Byte, Byte32, Byte32Builder}, + prelude::Builder, +}; +use rand::Rng; + +#[derive(Debug, Clone, Copy)] +pub struct ChannelId([u8; 32]); + +impl ChannelId { + pub fn new() -> Self { + ChannelId(Default::default()) + } + + pub fn new_random() -> Self { + ChannelId(rand::thread_rng().gen()) + } + + pub fn to_byte32(&self) -> Byte32 { + let mut byte32: [Byte; 32] = [0u8.into(); 32]; + let x = self.0; + let y = x.iter().map(|x| (*x).into()).collect::>(); + byte32.copy_from_slice(&y); + Byte32Builder::default().set(byte32).build() + } +} + +impl From<[u8; 32]> for ChannelId { + fn from(bytes: [u8; 32]) -> Self { + ChannelId(bytes) + } +} + +impl Default for ChannelId { + fn default() -> Self { + ChannelId([0u8; 32]) + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/client.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/client.rs new file mode 100644 index 0000000..cdc8aec --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/client.rs @@ -0,0 +1,259 @@ +use ckb_testtool::ckb_traits::CellDataProvider; + +use ckb_testtool::ckb_types::core::ScriptHashType; +use ckb_testtool::ckb_types::packed::{OutPoint, Script}; +use ckb_testtool::ckb_types::prelude::*; +use ckb_testtool::context::Context; + +use k256::ecdsa::signature::hazmat::PrehashSigner; +use perun_common::*; + +use perun_common::helpers::blake2b256; +use perun_common::perun_types::{ChannelState, ChannelStatus}; + +use crate::perun; +use crate::perun::harness; +use crate::perun::random; +use crate::perun::test; +use crate::perun::test::transaction::{AbortArgs, OpenResult}; +use crate::perun::test::{keys, transaction}; + +use k256::ecdsa::{Signature, SigningKey}; + +use super::cell::FundingCell; +use super::ChannelId; + +#[derive(Clone, Debug)] +pub struct Client { + index: u8, + signing_key: SigningKey, + name: String, +} + +impl Client { + pub fn new(idx: u8, name: String, sk: SigningKey) -> Client { + Client { + index: idx, + name, + signing_key: sk, + } + } + + // pubkey returns the public key of the client as a SEC1 encoded byte + // array. + pub fn pubkey(&self) -> [u8; 33] { + keys::verifying_key_to_byte_array(&self.signing_key.verifying_key()) + } + + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn open( + &self, + ctx: &mut Context, + env: &harness::Env, + funding_agreement: &test::FundingAgreement, + ) -> Result<(ChannelId, OpenResult), perun::Error> { + // Prepare environment so that this party has the required funds. + let inputs = + env.create_funds_from_agreement(ctx, self.index, funding_agreement)?; + // Create the channel token. + let (channel_token, channel_token_outpoint) = env.create_channel_token(ctx); + + let pcls = env.build_pcls(ctx, Default::default()); + let pcls_code_hash = pcls.code_hash(); + let pfls_code_hash = ctx + .get_cell_data_hash(&env.pfls_out_point) + .expect("pfls hash"); + let always_success_hash = ctx + .get_cell_data_hash(&env.always_success_out_point) + .expect("always success hash"); + + let parties = funding_agreement.mk_participants(ctx, env, env.min_capacity_no_script); + + let chan_params = perun_types::ChannelParametersBuilder::default() + .party_a(parties[0].clone()) + .party_b(parties[1].clone()) + .nonce(random::nonce().pack()) + .challenge_duration(env.challenge_duration.pack()) + .app(Default::default()) + .is_ledger_channel(ctrue!()) + .is_virtual_channel(cfalse!()) + .build(); + let cid_raw = blake2b256(chan_params.as_slice()); + let cid = ChannelId::from(cid_raw); + let chan_const = perun_types::ChannelConstantsBuilder::default() + .params(chan_params) + .pfls_code_hash(pfls_code_hash.clone()) + .pfls_hash_type(ScriptHashType::Data1.into()) + .pfls_min_capacity(env.min_capacity_pfls.pack()) + .pcls_code_hash(pcls_code_hash.clone()) + .pcls_hash_type(ScriptHashType::Data1.into()) + .thread_token(channel_token.clone()) + .build(); + + let pcts = env.build_pcts(ctx, chan_const.as_bytes()); + let pfls = env.build_pfls(ctx, pcts.calc_script_hash().as_bytes()); + + let args = transaction::OpenArgs { + cid, + funding_agreement: funding_agreement.clone(), + channel_token_outpoint: channel_token_outpoint.clone(), + inputs: inputs, + party_index: self.index, + pcls_script: pcls, + pcts_script: pcts, + pfls_script: pfls, + }; + let or = transaction::mk_open(ctx, env, args)?; + + let cycles = ctx.verify_tx(&or.tx, env.max_cycles)?; + println!("consumed cycles: {}", cycles); + Ok((cid, or)) + } + + pub fn fund( + &self, + ctx: &mut Context, + env: &harness::Env, + _cid: test::ChannelId, + funding_agreement: &test::FundingAgreement, + channel_cell: OutPoint, + channel_state: ChannelStatus, + pcts: Script, + ) -> Result { + // Prepare environment so that this party has the required funds. + let inputs = + env.create_funds_from_agreement(ctx, self.index, funding_agreement)?; + let fr = transaction::mk_fund( + ctx, + env, + transaction::FundArgs { + channel_cell, + funding_agreement: funding_agreement.clone(), + party_index: self.index, + state: channel_state, + inputs, + pcts, + }, + )?; + let cycles = ctx.verify_tx(&fr.tx, env.max_cycles)?; + println!("consumed cycles: {}", cycles); + Ok(fr) + } + + pub fn send(&self, ctx: &mut Context, env: &harness::Env) -> Result<(), perun::Error> { + Ok(()) + } + + pub fn sign(&self, state: ChannelState) -> Result, perun::Error> { + let s: Signature = self + .signing_key + .sign_prehash(&blake2b256(state.as_slice()))?; + Ok(Vec::from(s.to_der().as_bytes())) + } + + pub fn dispute( + &self, + ctx: &mut Context, + env: &harness::Env, + _cid: test::ChannelId, + channel_cell: OutPoint, + channel_state: ChannelStatus, + pcts: Script, + sigs: [Vec; 2], + ) -> Result { + let dr = transaction::mk_dispute( + ctx, + env, + transaction::DisputeArgs { + channel_cell, + state: channel_state, + party_index: self.index, + pcts_script: pcts, + sigs, + }, + )?; + let cycles = ctx.verify_tx(&dr.tx, env.max_cycles)?; + println!("consumed cycles: {}", cycles); + Ok(dr) + } + + pub fn abort( + &self, + ctx: &mut Context, + env: &harness::Env, + _cid: test::ChannelId, + state: ChannelStatus, + channel_cell: OutPoint, + funds: Vec, + ) -> Result { + let ar = transaction::mk_abort( + ctx, + env, + AbortArgs { + channel_cell, + funds, + state, + party_index: self.index, + }, + )?; + let cycles = ctx.verify_tx(&ar.tx, env.max_cycles)?; + println!("consumed cycles: {}", cycles); + Ok(ar) + } + + pub fn close( + &self, + ctx: &mut Context, + env: &harness::Env, + _cid: test::ChannelId, + channel_cell: OutPoint, + funds_cells: Vec, + state: ChannelStatus, + sigs: [Vec; 2], + ) -> Result { + let cr = transaction::mk_close( + ctx, + env, + transaction::CloseArgs { + channel_cell, + funds_cells, + party_index: self.index, + state, + sigs, + }, + )?; + let cycles = ctx.verify_tx(&cr.tx, env.max_cycles)?; + println!("consumed cycles: {}", cycles); + Ok(cr) + } + + pub fn force_close( + &self, + ctx: &mut Context, + env: &harness::Env, + _cid: test::ChannelId, + channel_cell: OutPoint, + funds_cells: Vec, + state: ChannelStatus, + ) -> Result { + // We will pass all available headers to the force close transaction. + let hs = ctx.headers.keys().cloned().collect(); + let fcr = transaction::mk_force_close( + ctx, + env, + transaction::ForceCloseArgs { + headers: hs, + channel_cell, + party_index: self.index, + funds_cells, + state, + }, + )?; + let cycles = ctx.verify_tx(&fcr.tx, env.max_cycles)?; + println!("consumed cycles: {}", cycles); + Ok(fcr) + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/funding_agreement.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/funding_agreement.rs new file mode 100644 index 0000000..5723b99 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/funding_agreement.rs @@ -0,0 +1,282 @@ +use ckb_occupied_capacity::Capacity; +use ckb_testtool::ckb_types::packed::{Byte as PackedByte, Script}; +use ckb_testtool::ckb_types::prelude::*; +use ckb_testtool::context::Context; +use ckb_types::bytes::Bytes; +use k256::elliptic_curve::sec1::ToEncodedPoint; +use k256::PublicKey; +use perun_common::perun_types::{ + self, Balances, CKByteDistribution, ParticipantBuilder, + SEC1EncodedPubKeyBuilder, SUDTAllocation, SUDTAsset, SUDTBalances, SUDTDistribution, +}; + +use crate::perun; + +#[derive(Debug, Clone)] +pub struct FundingAgreement { + entries: Vec, + register: AssetRegister, +} + +impl FundingAgreement { + pub fn register(&self) -> &AssetRegister { + &self.register + } + + pub fn has_udts(&self) -> bool { + self.register.len() > 0 + } + + pub fn new_with_capacities(caps: Vec<(P, u64)>) -> Self { + FundingAgreement { + entries: caps + .iter() + .enumerate() + .map(|(i, (acc, c))| FundingAgreementEntry { + ckbytes: *c, + sudts: Vec::new(), + index: i as u8, + pub_key: acc.public_key(), + }) + .collect(), + register: AssetRegister::new(), + } + } + + pub fn new_with_capacities_and_sudt( + caps: Vec<(P, u64)>, + asset: &Script, + max_cap: u64, + asset_amt: Vec<(P, u128)>, + ) -> Self { + let mut r = AssetRegister::new(); + let a = r.register_asset( + SUDTAsset::new_builder() + .type_script(asset.clone()) + .max_capacity(max_cap.pack()) + .build(), + ); + FundingAgreement { + entries: caps + .iter() + .enumerate() + .map(|(i, (acc, c))| FundingAgreementEntry { + ckbytes: *c, + sudts: vec![(a, asset_amt.get(i).unwrap().1)], + index: i as u8, + pub_key: acc.public_key(), + }) + .collect(), + register: r, + } + } + + pub fn content(&self) -> &Vec { + &self.entries + } + + pub fn mk_participants( + &self, + ctx: &mut Context, + env: &perun::harness::Env, + payment_min_capacity: Capacity, + ) -> Vec { + self.entries + .iter() + .map(|entry| { + let sec1_encoded_bytes: Vec<_> = entry + .pub_key + .to_encoded_point(true) + .as_bytes() + .iter() + .map(|b| PackedByte::new(*b)) + .collect(); + let sec1_pub_key = SEC1EncodedPubKeyBuilder::default() + .set(sec1_encoded_bytes.try_into().unwrap()) + .build(); + let unlock_script = ctx + .build_script( + &env.always_success_out_point, + // NOTE: To be able to make sure we can distinguish between the payout of + // the participants, we will pass their corresponding index as an argument. + // This will have no effect on the execution of the always_success_script, + // because it does not bother checking its arguments, but will allow us to + // assert the correct indices once a channel is concluded. + Bytes::from(vec![entry.index]), + ) + .expect("script"); + let unlock_script_hash = unlock_script.calc_script_hash(); + ParticipantBuilder::default() + // The payment script hash used to lock the funds after a channel close for + // this party. + .payment_script_hash(unlock_script_hash.clone()) + // The minimum capacity required for the payment cell to be valid. + .payment_min_capacity(payment_min_capacity.pack()) + // The unlock script hash used to identify this party. Normally this would be + // the lock args for a secp256k1 script or similar. Since we use the always + // success script, we will use the hash of said script parameterized by the + // party index. + .unlock_script_hash(unlock_script_hash.clone()) + .pub_key(sec1_pub_key) + .build() + }) + .collect() + } + + /// mk_balances creates a Balances object from the funding agreement where the given indices + /// already funded their part. + pub fn mk_balances(&self, indices: Vec) -> Result { + let mut ckbytes = [0u64; 2]; + let sudts = self.register.get_sudtassets(); + let mut sudt_dist: Vec<[u128; 2]> = Vec::new(); + for _ in 0..sudts.len() { + sudt_dist.push([0u128, 0]); + } + for fae in self.entries.iter() { + if indices.iter().find(|&&i| i == fae.index).is_none() { + continue; + } + + ckbytes[fae.index as usize] = fae.ckbytes; + for (asset, amount) in fae.sudts.iter() { + sudt_dist[asset.0 as usize][fae.index as usize] = *amount; + } + } + let mut sudt_alloc: Vec = Vec::new(); + for (i, asset) in sudts.iter().enumerate() { + sudt_alloc.push( + SUDTBalances::new_builder() + .asset(asset.clone()) + .distribution( + SUDTDistribution::new_builder() + .nth0(sudt_dist[i][0].pack()) + .nth1(sudt_dist[i][1].pack()) + .build(), + ) + .build(), + ); + } + + println!("mkbalances ckbytes: {:?}", ckbytes); + + Ok(Balances::new_builder() + .ckbytes( + CKByteDistribution::new_builder() + .nth0(ckbytes[0].pack()) + .nth1(ckbytes[1].pack()) + .build(), + ) + .sudts(SUDTAllocation::new_builder().set(sudt_alloc).build()) + .build()) + } + + pub fn expected_ckbytes_funding_for(&self, index: u8) -> Result { + let entry = self + .entries + .iter() + .find(|entry| entry.index == index) + .ok_or("unknown index")?; + Ok(entry.ckbytes) + } + + pub fn sudt_max_cap_sum(&self) -> u64 { + self.register.get_sudtassets().iter().fold(0u64, |old, asset| { + old + Capacity::shannons(asset.max_capacity().unpack()).as_u64() + }) + } + + pub fn expected_sudts_funding_for( + &self, + index: u8, + ) -> Result, perun::Error> { + let entry = self + .entries + .iter() + .find(|entry| entry.index == index) + .ok_or("unknown index")?; + entry + .sudts + .iter() + .map(|(asset, amount)| { + let sudt_asset = self.register.get_sudtasset(asset).ok_or("unknown asset")?; + let sudt_script = sudt_asset.type_script(); + let sudt_capacity = Capacity::shannons(sudt_asset.max_capacity().unpack()); + Ok((sudt_script, sudt_capacity, *amount)) + }) + .collect::, perun::Error>>() + } +} + +#[derive(Debug, Clone)] +pub struct FundingAgreementEntry { + pub ckbytes: u64, + pub sudts: Vec<(Asset, u128)>, + pub index: u8, + pub pub_key: PublicKey, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Asset(pub u32); + +impl Asset { + pub fn new() -> Self { + Asset(0) + } +} + +impl Default for Asset { + fn default() -> Self { + Asset(0) + } +} + +#[derive(Debug, Clone)] +pub struct AssetRegister { + assets: Vec<(Asset, SUDTAsset)>, +} + +impl AssetRegister { + fn new() -> Self { + AssetRegister { + assets: Vec::new(), + } + } + + pub fn len(&self) -> usize { + self.assets.len() + } + + pub fn register_asset(&mut self, sudt_asset: SUDTAsset) -> Asset { + let asset = Asset(self.assets.len() as u32); + self.assets.push((asset, sudt_asset)); + return asset; + } + pub fn get_sudtasset(&self, asset: &Asset) -> Option<&SUDTAsset> { + match self.assets.get(asset.0 as usize) { + Some((_, sudt_asset)) => Some(sudt_asset), + None => None, + } + } + + pub fn get_asset(&self, sudt_asset: SUDTAsset) -> Option<&Asset> { + match self.assets.iter().find(|(_, a)| a.as_slice()[..] == sudt_asset.as_slice()[..]) { + Some((asset, _)) => Some(asset), + None => None, + } + } + + pub fn guess_asset_from_script(&self, script: &Script) -> Option<&Asset> { + match self + .assets + .iter() + .find(|(_, sudt_asset)| sudt_asset.type_script().as_slice()[..] == script.as_slice()[..]) + { + Some((asset, _)) => Some(asset), + None => None, + } + } + + pub fn get_sudtassets(&self) -> Vec { + self.assets.iter().map(|(_, a)| a.clone()).collect() + } +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/keys.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/keys.rs new file mode 100644 index 0000000..00c507d --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/keys.rs @@ -0,0 +1,11 @@ +use k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}; + +pub fn verifying_key_to_byte_array(vk: &VerifyingKey) -> [u8; 33] { + vk.to_encoded_point(true) + .as_bytes() + .iter() + .map(|x| *x) + .collect::>() + .try_into() + .expect("public-key length 33") +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/mod.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/mod.rs new file mode 100644 index 0000000..effc4c1 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/mod.rs @@ -0,0 +1,15 @@ +#[cfg(test)] +mod client; +pub use client::*; + +mod funding_agreement; +pub use funding_agreement::*; + +mod channel_id; +pub use channel_id::*; + +pub mod keys; + +pub mod transaction; + +pub mod cell; diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/abort.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/abort.rs new file mode 100644 index 0000000..c454be0 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/abort.rs @@ -0,0 +1,85 @@ +use ckb_testtool::{ + ckb_types::{ + bytes::Bytes, + core::{TransactionBuilder, TransactionView}, + packed::{CellInput, OutPoint}, + prelude::{Builder, Entity, Pack}, + }, + context::Context, +}; +use perun_common::{perun_types::ChannelStatus, redeemer}; + +use crate::perun::{self, harness, test::{cell::FundingCell, transaction::common::add_cap_to_a}}; + +use super::common::{channel_witness, create_cells}; + +#[derive(Debug, Clone)] +pub struct AbortArgs { + pub channel_cell: OutPoint, + pub funds: Vec, + pub state: ChannelStatus, + pub party_index: u8, +} + +#[derive(Debug, Clone)] +pub struct AbortResult { + pub tx: TransactionView, +} + +impl Default for AbortResult { + fn default() -> Self { + AbortResult { + tx: TransactionBuilder::default().build(), + } + } +} + +pub fn mk_abort( + ctx: &mut Context, + env: &harness::Env, + args: AbortArgs, +) -> Result { + let payment_input = env.create_min_cell_for_index(ctx, args.party_index); + let abort_action = redeemer!(Abort); + let witness_args = channel_witness!(abort_action); + let mut inputs = vec![ + CellInput::new_builder() + .previous_output(args.channel_cell) + .build(), + CellInput::new_builder() + .previous_output(payment_input) + .build(), + ]; + inputs.extend(args.funds.iter().cloned().map(|op| { + CellInput::new_builder() + .previous_output(op.outpoint()) + .build() + })); + + let headers: Vec<_> = ctx.headers.keys().cloned().collect(); + // TODO: We are expecting the output amounts to be greater than the minimum amount necessary to + // accomodate the space required for each output cell. + let f = |idx| env.build_lock_script(ctx, Bytes::from(vec![idx])); + let channel_cap = env.min_capacity_for_channel(args.state.clone())?; + let balances = add_cap_to_a(&args.state.state().balances(), channel_cap); + let outputs = balances.mk_outputs(f, vec![0]); + let outputs_data: Vec<_> = outputs.iter().map(|o| o.1.clone()).collect(); + + let cell_deps = vec![ + env.pcls_script_dep.clone(), + env.pcts_script_dep.clone(), + env.always_success_script_dep.clone(), + ]; + + let rtx = TransactionBuilder::default() + .inputs(inputs) + .outputs(outputs.iter().cloned().map(|o| o.0)) + .outputs_data(outputs_data.pack()) + .cell_deps(cell_deps) + .header_deps(headers) + .witness(witness_args.as_bytes().pack()) + .build(); + let tx = ctx.complete_tx(rtx); + create_cells(ctx, tx.hash(), outputs); + Ok(AbortResult { tx }) +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/close.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/close.rs new file mode 100644 index 0000000..e40f6f7 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/close.rs @@ -0,0 +1,96 @@ +use ckb_testtool::{ + ckb_types::packed::{CellInput, OutPoint}, + ckb_types::{ + bytes::Bytes, + core::{TransactionBuilder, TransactionView}, + prelude::{Builder, Entity, Pack}, + }, + context::Context, +}; +use perun_common::{close, perun_types::ChannelStatus, redeemer}; + +use crate::perun::{ + self, harness, + test::{cell::FundingCell, transaction::common::channel_witness}, +}; + +use super::common::{create_cells, add_cap_to_a}; + +#[derive(Debug, Clone)] +pub struct CloseArgs { + /// The channel cell which tracks the channel on-chain. + pub channel_cell: OutPoint, + /// All funding cells used to initially fund the channel. + pub funds_cells: Vec, + /// The channel state which shall be used for closing. + pub state: ChannelStatus, + /// The DER encoded signatures for the channel state in proper order of parties. + pub sigs: [Vec; 2], + pub party_index: u8, +} + +#[derive(Debug, Clone)] +pub struct CloseResult { + pub tx: TransactionView, +} + +impl Default for CloseResult { + fn default() -> Self { + CloseResult { + tx: TransactionBuilder::default().build(), + } + } +} + +pub fn mk_close( + ctx: &mut Context, + env: &harness::Env, + args: CloseArgs, +) -> Result { + let payment_input = env.create_min_cell_for_index(ctx, args.party_index); + let mut inputs = vec![ + CellInput::new_builder() + .previous_output(args.channel_cell) + .build(), + CellInput::new_builder() + .previous_output(payment_input) + .build(), + ]; + inputs.extend(args.funds_cells.iter().cloned().map(|f| { + CellInput::new_builder() + .previous_output(f.outpoint()) + .build() + })); + + let cell_deps = vec![ + env.pcls_script_dep.clone(), + env.pcts_script_dep.clone(), + env.pfls_script_dep.clone(), + env.always_success_script_dep.clone(), + ]; + let channel_cap = env.min_capacity_for_channel(args.state.clone())?; + let balances = add_cap_to_a(&args.state.state().balances(), channel_cap); + let f = |idx| env.build_lock_script(ctx, Bytes::from(vec![idx])); + let outputs = balances.mk_outputs(f, vec![0, 1]); + let outputs_data: Vec<_> = outputs.iter().map(|o| o.1.clone()).collect(); + + let close_action = redeemer!(close!( + args.state.state(), + args.sigs[0].pack(), + args.sigs[1].pack() + )); + let witness_args = channel_witness!(close_action); + + let headers: Vec<_> = ctx.headers.keys().cloned().collect(); + let rtx = TransactionBuilder::default() + .inputs(inputs) + .outputs(outputs.iter().map(|o| o.0.clone())) + .outputs_data(outputs_data.pack()) + .witness(witness_args.as_bytes().pack()) + .cell_deps(cell_deps) + .header_deps(headers) + .build(); + let tx = ctx.complete_tx(rtx); + create_cells(ctx, tx.hash(), outputs); + Ok(CloseResult { tx }) +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/common.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/common.rs new file mode 100644 index 0000000..3814aad --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/common.rs @@ -0,0 +1,41 @@ +use ckb_occupied_capacity::Capacity; +use ckb_testtool::{ + bytes, + ckb_types::{packed::{Byte32, CellOutput, OutPoint}, prelude::{Unpack, Pack}}, + context::Context, +}; +use molecule::prelude::{Entity, Builder}; +use perun_common::perun_types::Balances; + +use crate::perun; + +/// Build witness args containing the given action. +macro_rules! channel_witness { + ($action:expr) => { + ckb_testtool::ckb_types::packed::WitnessArgsBuilder::default() + .input_type(Some($action.as_bytes()).pack()) + .build() + }; +} +pub(crate) use channel_witness; + +pub fn create_funding_from( + available_capacity: Capacity, + wanted_capacity: Capacity, +) -> Result { + Ok(available_capacity.safe_sub(wanted_capacity)?) +} + +pub fn create_cells(ctx: &mut Context, hash: Byte32, outputs: Vec<(CellOutput, bytes::Bytes)>) { + for (i, (output, data)) in outputs.into_iter().enumerate() { + let out_point = OutPoint::new(hash.clone(), i as u32); + ctx.create_cell_with_out_point(out_point, output, data); + } +} + +pub fn add_cap_to_a(balances: &Balances, cap: Capacity) -> Balances { + let bal_a: u64 = balances.ckbytes().nth0().unpack(); + balances.clone().as_builder().ckbytes( + balances.ckbytes().as_builder().nth0( + (cap.as_u64() + bal_a).pack()).build()).build() +} \ No newline at end of file diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/dispute.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/dispute.rs new file mode 100644 index 0000000..aa8a5e4 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/dispute.rs @@ -0,0 +1,94 @@ +use ckb_testtool::{ + ckb_types::packed::{CellInput, CellOutput, OutPoint}, + ckb_types::{ + core::{TransactionBuilder, TransactionView}, + packed::Script, + prelude::{Builder, Entity, Pack}, + }, + context::Context, +}; +use perun_common::{dispute, perun_types::ChannelStatus, redeemer}; + +use crate::perun::{self, harness, test::transaction::common::channel_witness}; + +use super::common::create_cells; + +#[derive(Debug, Clone)] +pub struct DisputeArgs { + /// The channel cell which tracks the channel on-chain. + pub channel_cell: OutPoint, + /// The channel state which shall be used for closing. + pub state: ChannelStatus, + /// The DER encoded signatures for the channel state in proper order of parties. + pub sigs: [Vec; 2], + /// The Perun channel type script used for the current channel. + pub pcts_script: Script, + pub party_index: u8, +} + +#[derive(Debug, Clone)] +pub struct DisputeResult { + pub tx: TransactionView, + pub channel_cell: OutPoint, +} + +impl Default for DisputeResult { + fn default() -> Self { + DisputeResult { + tx: TransactionBuilder::default().build(), + channel_cell: OutPoint::default(), + } + } +} + +pub fn mk_dispute( + ctx: &mut Context, + env: &harness::Env, + args: DisputeArgs, +) -> Result { + let payment_input = env.create_min_cell_for_index(ctx, args.party_index); + let inputs = vec![ + CellInput::new_builder() + .previous_output(args.channel_cell) + .build(), + CellInput::new_builder() + .previous_output(payment_input) + .build(), + ]; + + let cell_deps = vec![ + env.pcls_script_dep.clone(), + env.pcts_script_dep.clone(), + env.pfls_script_dep.clone(), + env.always_success_script_dep.clone(), + ]; + + let pcls_script = env.build_pcls(ctx, Default::default()); + let capacity_for_cs = env.min_capacity_for_channel(args.state.clone())?; + let channel_cell = CellOutput::new_builder() + .capacity(capacity_for_cs.pack()) + .lock(pcls_script.clone()) + .type_(Some(args.pcts_script.clone()).pack()) + .build(); + let outputs = vec![(channel_cell.clone(), args.state.as_bytes())]; + let outputs_data: Vec<_> = outputs.iter().map(|e| e.1.clone()).collect(); + + let dispute_action = redeemer!(dispute!(args.sigs[0].pack(), args.sigs[1].pack())); + let witness_args = channel_witness!(dispute_action); + + let headers: Vec<_> = ctx.headers.keys().cloned().collect(); + let rtx = TransactionBuilder::default() + .inputs(inputs) + .outputs(outputs.iter().map(|e| e.0.clone())) + .outputs_data(outputs_data.pack()) + .header_deps(headers) + .witness(witness_args.as_bytes().pack()) + .cell_deps(cell_deps) + .build(); + let tx = ctx.complete_tx(rtx); + create_cells(ctx, tx.hash(), outputs); + Ok(DisputeResult { + channel_cell: OutPoint::new(tx.hash(), 0), + tx, + }) +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/force_close.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/force_close.rs new file mode 100644 index 0000000..a767ab5 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/force_close.rs @@ -0,0 +1,94 @@ +use ckb_testtool::{ + ckb_types::packed::{CellInput, OutPoint}, + ckb_types::{ + bytes::Bytes, + core::{TransactionBuilder, TransactionView}, + packed::Byte32, + prelude::{Builder, Entity, Pack}, + }, + context::Context, +}; +use perun_common::{perun_types::ChannelStatus, redeemer}; + +use crate::perun::{ + self, harness, + test::{cell::FundingCell, transaction::common::channel_witness}, +}; + +use super::common::{create_cells, add_cap_to_a}; + +#[derive(Debug, Clone)] +pub struct ForceCloseArgs { + /// The channel cell which tracks the channel on-chain. + pub channel_cell: OutPoint, + /// The latest headers for the chain containing some timestamps. + pub headers: Vec, + /// All funding cells used to initially fund the channel. + pub funds_cells: Vec, + /// The channel state which shall be used for closing. + pub state: ChannelStatus, + pub party_index: u8, +} + +#[derive(Debug, Clone)] +pub struct ForceCloseResult { + pub tx: TransactionView, +} + +impl Default for ForceCloseResult { + fn default() -> Self { + ForceCloseResult { + tx: TransactionBuilder::default().build(), + } + } +} + +pub fn mk_force_close( + ctx: &mut Context, + env: &harness::Env, + args: ForceCloseArgs, +) -> Result { + let payment_input = env.create_min_cell_for_index(ctx, args.party_index); + let mut inputs = vec![ + CellInput::new_builder() + .previous_output(args.channel_cell) + .build(), + CellInput::new_builder() + .previous_output(payment_input) + .build(), + ]; + inputs.extend(args.funds_cells.iter().cloned().map(|f| { + CellInput::new_builder() + .previous_output(f.outpoint()) + .build() + })); + + let cell_deps = vec![ + env.pcls_script_dep.clone(), + env.pcts_script_dep.clone(), + env.pfls_script_dep.clone(), + env.always_success_script_dep.clone(), + ]; + + // Rust... + let channel_cap = env.min_capacity_for_channel(args.state.clone())?; + let balances = add_cap_to_a(&args.state.state().balances(), channel_cap); + let f = |idx| env.build_lock_script(ctx, Bytes::from(vec![idx])); + let outputs = balances.mk_outputs(f, vec![0, 1]); + let outputs_data: Vec<_> = outputs.iter().map(|o| o.1.clone()).collect(); + + let force_close_action = redeemer!(ForceClose); + let witness_args = channel_witness!(force_close_action); + + let rtx = TransactionBuilder::default() + .inputs(inputs) + .outputs(outputs.iter().map(|o| o.0.clone())) + .outputs_data(outputs_data.pack()) + .header_deps(args.headers) + .witness(witness_args.as_bytes().pack()) + .cell_deps(cell_deps) + .build(); + let tx = ctx.complete_tx(rtx); + create_cells(ctx, tx.hash(), outputs); + Ok(ForceCloseResult { tx }) +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/fund.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/fund.rs new file mode 100644 index 0000000..15bd968 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/fund.rs @@ -0,0 +1,123 @@ +use ckb_occupied_capacity::{Capacity, IntoCapacity}; +use ckb_testtool::{ + ckb_types::{ + bytes::Bytes, + core::{TransactionBuilder, TransactionView}, + packed::{CellInput, CellOutput, OutPoint, Script}, + prelude::{Builder, Entity, Pack}, + }, + context::Context, +}; +use perun_common::{fund, perun_types::ChannelStatus, redeemer}; + +use crate::perun::{ + self, harness, + test::{cell::{FundingCell, mk_funding_cell}, FundingAgreement}, +}; + +use super::common::{channel_witness, create_cells, create_funding_from}; + +#[derive(Debug, Clone)] +pub struct FundArgs { + pub channel_cell: OutPoint, + pub funding_agreement: FundingAgreement, + pub party_index: u8, + pub inputs: Vec<(OutPoint, Capacity)>, + pub pcts: Script, + pub state: ChannelStatus, +} + +#[derive(Debug, Clone)] +pub struct FundResult { + pub tx: TransactionView, + pub channel_cell: OutPoint, + pub funds_cells: Vec, + pub state: ChannelStatus, +} + +impl Default for FundResult { + fn default() -> Self { + FundResult { + tx: TransactionBuilder::default().build(), + channel_cell: OutPoint::default(), + funds_cells: vec![], + state: ChannelStatus::default(), + } + } +} + +pub fn mk_fund( + ctx: &mut Context, + env: &harness::Env, + args: FundArgs, +) -> Result { + let fund_action = redeemer!(fund!()); + let witness_args = channel_witness!(fund_action); + let wanted = args + .funding_agreement + .expected_ckbytes_funding_for(args.party_index)?; + let pfls = env.build_pfls(ctx, args.pcts.calc_script_hash().as_bytes()); + // TODO: Make sure enough funds available all cells! + + // Note: we do not really need to shrink the balances to only contain the party's balances, as balances.mk_outputs will do so anyway. + let balances = args.funding_agreement.mk_balances(vec![args.party_index])?; + let pfls = |_| pfls.clone(); + let mut outputs = balances.mk_outputs(pfls, vec![1]); + let num_fund_ouputs = outputs.len(); + + let my_available_funds = Capacity::shannons(args.inputs.iter().map(|(_, c)| c.as_u64()).sum()); + let exchange_cell = create_funding_from(my_available_funds, (wanted + args.funding_agreement.sudt_max_cap_sum()).into_capacity())?; + let mut inputs = vec![ + CellInput::new_builder() + .previous_output(args.channel_cell) + .build(), + ]; + for (outpoint, _) in args.inputs.iter() { + inputs.push(CellInput::new_builder().previous_output(outpoint.clone()).build()); + } + // NOTE: mk_fund currently expects the be called for the last party funding the channel. + // Otherwise the call to `mk_funded` returns a wrong channel state. + let updated_cs = args.state.mk_funded(); + let capacity_for_new_cs = env.min_capacity_for_channel(updated_cs.clone())?; + let pcls = env.build_pcls(ctx, Default::default()); + let new_channel_cell = CellOutput::new_builder() + .capacity(capacity_for_new_cs.pack()) + .lock(pcls.clone()) + .type_(Some(args.pcts.clone()).pack()) + .build(); + outputs.append(&mut vec![ + (new_channel_cell.clone(), updated_cs.as_bytes()), + ( + CellOutput::new_builder() + .capacity(exchange_cell.pack()) + .lock(env.build_lock_script(ctx, Bytes::from(vec![args.party_index]))) + .build(), + Bytes::new(), + ), + ]); + let outputs_data: Vec<_> = outputs.iter().map(|o| o.1.clone()).collect(); + let cell_deps = vec![ + env.always_success_script_dep.clone(), + env.pcts_script_dep.clone(), + env.pcls_script_dep.clone(), + env.sample_udt_script_dep.clone(), // TODO: Make this generic + ]; + let headers: Vec<_> = ctx.headers.keys().cloned().collect(); + let rtx = TransactionBuilder::default() + .inputs(inputs) + .witness(witness_args.as_bytes().pack()) + .outputs(outputs.clone().into_iter().map(|o| o.0.clone())) + .outputs_data(outputs_data.pack()) + .cell_deps(cell_deps) + .header_deps(headers) + .build(); + let tx = ctx.complete_tx(rtx); + create_cells(ctx, tx.hash(), outputs.clone()); + Ok(FundResult { + channel_cell: OutPoint::new(tx.hash(), num_fund_ouputs as u32), + funds_cells: outputs[..num_fund_ouputs].iter().enumerate().map(|(i, (co, bytes))| + mk_funding_cell(args.party_index, OutPoint::new(tx.hash(), i as u32), co, bytes.clone(), args.funding_agreement.register())).collect(), + state: updated_cs, + tx, + }) +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/mod.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/mod.rs new file mode 100644 index 0000000..b1d6673 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/mod.rs @@ -0,0 +1,19 @@ +mod open; +pub use open::*; + +mod fund; +pub use fund::*; + +mod abort; +pub use abort::*; + +mod close; +pub use close::*; + +mod force_close; +pub use force_close::*; + +mod dispute; +pub use dispute::*; + +mod common; diff --git a/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/open.rs b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/open.rs new file mode 100644 index 0000000..808cd8e --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/perun/test/transaction/open.rs @@ -0,0 +1,129 @@ +use std::vec; + +use ckb_occupied_capacity::{Capacity, IntoCapacity}; +use ckb_testtool::{ + ckb_types::{ + bytes::Bytes, + core::{TransactionBuilder, TransactionView}, + packed::{CellInput, CellOutput, OutPoint, Script}, + prelude::{Builder, Entity, Pack}, + }, + context::Context, +}; +use perun_common::perun_types::ChannelStatus; + +use crate::perun::{ + self, harness, + test::{cell::{FundingCell, mk_funding_cell}, ChannelId, FundingAgreement}, +}; + +use super::common::{create_cells, create_funding_from}; + +#[derive(Clone)] +pub struct OpenArgs { + pub cid: ChannelId, + pub funding_agreement: FundingAgreement, + pub channel_token_outpoint: OutPoint, + pub inputs: Vec<(OutPoint, Capacity)>, + pub party_index: u8, + pub pcls_script: Script, + pub pcts_script: Script, + pub pfls_script: Script, +} + +pub struct OpenResult { + pub tx: TransactionView, + pub channel_cell: OutPoint, + pub funds_cells: Vec, + pub pcts: Script, + pub state: ChannelStatus, +} + +impl Default for OpenResult { + fn default() -> Self { + OpenResult { + tx: TransactionBuilder::default().build(), + channel_cell: OutPoint::default(), + funds_cells: Vec::new(), + pcts: Script::default(), + state: ChannelStatus::default(), + } + } +} + +pub fn mk_open( + ctx: &mut Context, + env: &harness::Env, + args: OpenArgs, +) -> Result { + let mut inputs = vec![ + CellInput::new_builder() + .previous_output(args.channel_token_outpoint) + .build(), + ]; + for (outpoint, _) in args.inputs.iter() { + inputs.push( + CellInput::new_builder() + .previous_output(outpoint.clone()) + .build(), + ); + } + let initial_cs = + env.build_initial_channel_state(args.cid, args.party_index, &args.funding_agreement)?; + let capacity_for_cs = env.min_capacity_for_channel(initial_cs.clone())?; + let channel_cell = CellOutput::new_builder() + .capacity(capacity_for_cs.pack()) + .lock(args.pcls_script.clone()) + .type_(Some(args.pcts_script.clone()).pack()) + .build(); + let wanted = args + .funding_agreement + .expected_ckbytes_funding_for(args.party_index)?; + + let pfls = |_| args.pfls_script.clone(); + + let balances = args.funding_agreement.mk_balances(vec![args.party_index])?; + let mut outputs = balances.mk_outputs(pfls, vec![0]); + let num_of_funds = outputs.len(); + // TODO: Make sure enough funds available all cells! + let my_available_funds = Capacity::shannons(args.inputs.iter().map(|(_, c)| c.as_u64()).sum()); + let exchange_cell_cap = create_funding_from(my_available_funds, (wanted + args.funding_agreement.sudt_max_cap_sum()).into_capacity())?; + // NOTE: The ORDER here is important. We need to reference the outpoints later on by using the + // correct index in the output array of the transaction we build. + outputs.append( + vec![ + (channel_cell.clone(), initial_cs.as_bytes()), + ( + CellOutput::new_builder() + .capacity(exchange_cell_cap.pack()) + .lock(env.build_lock_script(ctx, Bytes::from(vec![args.party_index]))) + .build(), + Bytes::new(), + ), + ].as_mut() + ); + + let outputs_data: Vec<_> = outputs.iter().map(|o| o.1.clone()).collect(); + let cell_deps = vec![ + env.always_success_script_dep.clone(), + env.pcts_script_dep.clone(), + env.sample_udt_script_dep.clone(), // TODO: Make this generic! + ]; + let rtx = TransactionBuilder::default() + .inputs(inputs) + .outputs(outputs.iter().map(|o| o.0.clone())) + .outputs_data(outputs_data.pack()) + .cell_deps(cell_deps) + .build(); + let tx = ctx.complete_tx(rtx); + create_cells(ctx, tx.hash(), outputs.clone()); + Ok(OpenResult { + // See NOTE above for magic indices. + channel_cell: OutPoint::new(tx.hash(), num_of_funds as u32), + funds_cells: outputs[..num_of_funds].iter().enumerate().map(|(i, (co, bytes))| + mk_funding_cell(args.party_index, OutPoint::new(tx.hash(), i as u32), co, bytes.clone(), args.funding_agreement.register())).collect(), + tx, + pcts: args.pcts_script, + state: initial_cs, + }) +} diff --git a/payment-channel-ckb/devnet/contracts/tests/src/tests.rs b/payment-channel-ckb/devnet/contracts/tests/src/tests.rs new file mode 100644 index 0000000..f21be32 --- /dev/null +++ b/payment-channel-ckb/devnet/contracts/tests/src/tests.rs @@ -0,0 +1,486 @@ +use crate::perun::mutators::*; +use crate::perun::random; + +use super::*; +use ckb_occupied_capacity::Capacity; +use ckb_testtool::ckb_types::{bytes::Bytes, packed::*, prelude::*}; +use ckb_testtool::context::Context; +use perun; +use perun::test; +use perun_common::helpers::blake2b256; +use perun_common::perun_types::{Balances, Bool, ChannelState, SEC1EncodedPubKey, CKByteDistribution}; +use perun_common::sig::verify_signature; + +const MAX_CYCLES: u64 = 10 * 10_000_000; +const CHALLENGE_DURATION_MS: u64 = 10 * 1000; + +#[test] +fn test_signature() { + // This tests the interoperability between the on-chain signature verification + // and the key generation & signing in the perun-ckb-backend's wallet. + + // This signature was generated by the wallet in the perun-ckb-backend + let sig_string = "3045022100a4f8768be2e5afdcbcfee600eb963caf1957d32edca49390e6f5a4933c2f6dcd02207fd9d2b5928266e9aeee039285508da1dbdbeec67cb995fd8735e1795bf53e5f"; + let sig = hex::decode(sig_string).expect("decoding signature"); + let sig_bytes: Bytes = sig.into(); + + // This public key was generated by the wallet in the perun-ckb-backend + let pubkey_string = "02d1ab4e7cbfb2262de6f3f816d9b044970162a6a2ae0e6b0ff9b082e315c5e152"; + let pubkey = hex::decode(pubkey_string).expect("decoding pubkey"); + let pubkey_bytes: [Byte; 33] = pubkey + .iter() + .map(|x| (*x).into()) + .collect::>() + .try_into() + .unwrap(); + SEC1EncodedPubKey::new_builder().set(pubkey_bytes).build(); + + let balances_array: [Uint64; 2] = [10u64.pack(), 11u64.pack()]; + let balances = Balances::new_builder().ckbytes(CKByteDistribution::new_builder().set(balances_array).build()).build(); + let channel_state = ChannelState::new_builder() + .channel_id(Byte32::zero()) + .balances(balances) + .is_final(Bool::from_bool(true)) + .version(10u64.pack()) + .build(); + let msg = channel_state.as_slice(); + let msg_hash = blake2b256(msg); + + verify_signature(&msg_hash, &sig_bytes, pubkey.as_slice()).expect("valid signature"); +} + +// TODO: Add mutator to channel state that can be passed to dispute, and close. +#[test] +fn channel_test_bench() -> Result<(), perun::Error> { + let res = [ + test_funding_abort, + test_successful_funding_with_udt, + test_successful_funding_without_udt, + test_early_force_close, + test_close, + test_force_close, + test_multiple_disputes, + test_multiple_disputes_same_version, + test_multi_asset_payment, + test_multi_asset_abort, + test_multi_asset_abort_zero_sudt_balance, + test_multi_asset_force_close, + ] + .iter() + .map(|test| { + let mut context = Context::default(); + let pe = perun::harness::Env::new(&mut context, MAX_CYCLES, CHALLENGE_DURATION_MS) + .expect("preparing environment"); + test(&mut context, &pe) + }) + .collect::>(); + res.into_iter().collect() +} + +fn create_channel_test( + context: &mut Context, + env: &perun::harness::Env, + parts: &[perun::TestAccount], + test: impl Fn(&mut perun::channel::Channel) -> Result<(), perun::Error>, +) -> Result<(), perun::Error> { + let mut chan = perun::channel::Channel::new(context, env, parts); + test(&mut chan) +} + +fn test_funding_abort( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding_timeout = 10; + let funding = [ + Capacity::bytes(1000)?.as_u64(), + Capacity::bytes(1000)?.as_u64(), + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.delay(funding_timeout); + + chan.with(alice).abort().expect("aborting channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_successful_funding_without_udt( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.assert(); + Ok(()) + }) +} + + +fn test_successful_funding_with_udt( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let asset_funding = [ + 20u128, + 30u128, + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities_and_sudt( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + &env.sample_udt_script, + env.sample_udt_max_cap.as_u64(), + parts.iter().cloned().zip(asset_funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_close(context: &mut Context, env: &perun::harness::Env) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.with(alice) + .finalize() + .close() + .expect("closing channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_force_close(context: &mut Context, env: &perun::harness::Env) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.with(bob).dispute().expect("invalid channel dispute"); + + chan.delay(env.challenge_duration); + + chan.with(bob).force_close().expect("force closing channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_early_force_close(context: &mut Context, env: &perun::harness::Env) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.with(bob).dispute().expect("invalid channel dispute"); + + chan.with(bob).invalid().force_close().expect("force closing channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_multiple_disputes_same_version( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.with(alice) + .valid() + .dispute() + .expect("disputing channel"); + + chan.with(bob) + .invalid() + .dispute() + .expect("disputing channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_multiple_disputes( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.with(alice) + .valid() + .dispute() + .expect("disputing channel"); + + chan.with(bob) + .valid() + .update(bump_version()) + .dispute() + .expect("disputing channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_multi_asset_payment( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let asset_funding = [ + 20u128, + 30u128, + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities_and_sudt( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + &env.sample_udt_script, + env.sample_udt_max_cap.as_u64(), + parts.iter().cloned().zip(asset_funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.update(pay_ckbytes(Direction::AtoB, 50)); + chan.update(pay_sudt(Direction::BtoA, 10, 0)); + + chan.with(alice).finalize().close().expect("closing channel"); + + chan.assert(); + Ok(()) + }) +} + +pub fn test_multi_asset_abort( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(0)?.as_u64(), + Capacity::bytes(0)?.as_u64(), + ]; + let asset_funding = [ + 30u128, + 20u128, + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities_and_sudt( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + &env.sample_udt_script, + env.sample_udt_max_cap.as_u64(), + parts.iter().cloned().zip(asset_funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(alice).abort().expect("aborting channel"); + + chan.assert(); + Ok(()) + }) +} + +pub fn test_multi_asset_abort_zero_sudt_balance( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(0)?.as_u64(), + Capacity::bytes(0)?.as_u64(), + ]; + let asset_funding = [ + 0u128, + 0u128, + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities_and_sudt( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + &env.sample_udt_script, + env.sample_udt_max_cap.as_u64(), + parts.iter().cloned().zip(asset_funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(alice).abort().expect("aborting channel"); + + chan.assert(); + Ok(()) + }) +} + +fn test_multi_asset_force_close( + context: &mut Context, + env: &perun::harness::Env, +) -> Result<(), perun::Error> { + let (alice, bob) = ("alice", "bob"); + let parts = [random::account(alice), random::account(bob)]; + let funding = [ + Capacity::bytes(100)?.as_u64(), + Capacity::bytes(100)?.as_u64(), + ]; + let asset_funding = [ + 20u128, + 30u128, + ]; + let funding_agreement = test::FundingAgreement::new_with_capacities_and_sudt( + parts.iter().cloned().zip(funding.iter().cloned()).collect(), + &env.sample_udt_script, + env.sample_udt_max_cap.as_u64(), + parts.iter().cloned().zip(asset_funding.iter().cloned()).collect(), + ); + create_channel_test(context, env, &parts, |chan| { + chan.with(alice) + .open(&funding_agreement) + .expect("opening channel"); + + chan.with(bob) + .fund(&funding_agreement) + .expect("funding channel"); + + chan.update(pay_ckbytes(Direction::AtoB, 50)); + chan.update(pay_sudt(Direction::BtoA, 10, 0)); + + chan.with(bob).dispute().expect("disputing channel"); + + chan.delay(env.challenge_duration); + + chan.with(bob).force_close().expect("force closing channel"); + + chan.assert(); + Ok(()) + }) +} diff --git a/payment-channel-ckb/devnet/deploy_contracts.sh b/payment-channel-ckb/devnet/deploy_contracts.sh new file mode 100755 index 0000000..b93ca2f --- /dev/null +++ b/payment-channel-ckb/devnet/deploy_contracts.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -eu +[ -n "${DEBUG:-}" ] && set -x || true + +ACCOUNTS_DIR="accounts" +PERUN_CONTRACTS_DIR="contracts" +SYSTEM_SCRIPTS_DIR="system_scripts" +DEVNET_DIR="$PWD" + +genesis=$(cat $ACCOUNTS_DIR/genesis-2.txt | awk '/testnet/ { count++; if (count == 2) print $2}') + +cd $PERUN_CONTRACTS_DIR + +if [ -d "migrations/dev" ]; then + rm -rf "migrations/dev" +fi + +expect << EOF +spawn capsule deploy --address $genesis --api "http://127.0.0.1:8114" --fee 1 +expect "Confirm deployment? (Yes/No)" +send "Yes\r" +expect "Password:" +send "\r" +expect eof +EOF + +# Fetch default contracts: +cd $DEVNET_DIR + +if [ -d "$SYSTEM_SCRIPTS_DIR" ]; then + rm -rf "$SYSTEM_SCRIPTS_DIR" +fi + +mkdir -p "$SYSTEM_SCRIPTS_DIR" +## jq will interpret the code_hash and tx_hash as numbers, so we need to wrap them in quotes. +## The index must also be a string value, but yaml does not support hex values as a top level block argument +## so we have to do that in a second pass... +ckb-cli util genesis-scripts --output-format json \ + | sed 's/code_hash: \(.*\)/code_hash: \"\1\"/; s/tx_hash: \(.*\)/tx_hash: \"\1\"/' \ + | sed 's/"index": \([0-9]\+\),/echo "\\"index\\": $(python -c "print(\\\"\\\\\\"{}\\\\\\"\\\".format(hex(\1)))"),";/e' \ + | jq . > "$SYSTEM_SCRIPTS_DIR/default_scripts.json" + +cd $DEVNET_DIR + +SUDT_TX_HASH=$(cat ./contracts/migrations/dev/*.json | jq .cell_recipes[3].tx_hash) +SUDT_TX_INDEX=$(cat ./contracts/migrations/dev/*.json | jq .cell_recipes[3].index) +SUDT_DATA_HASH=$(cat ./contracts/migrations/dev/*.json | jq .cell_recipes[3].data_hash) + +# TODO: This only works as long as the tx index is 0-9. +jq ".items.sudt.script_id.code_hash = $SUDT_DATA_HASH | .items.sudt.cell_dep.out_point.tx_hash = $SUDT_TX_HASH | .items.sudt.cell_dep.out_point.index = \"0x$SUDT_TX_INDEX\"" ./sudt-celldep-template.json > $SYSTEM_SCRIPTS_DIR/sudt-celldep.json diff --git a/payment-channel-ckb/devnet/devnet-session.yaml b/payment-channel-ckb/devnet/devnet-session.yaml new file mode 100644 index 0000000..c266d19 --- /dev/null +++ b/payment-channel-ckb/devnet/devnet-session.yaml @@ -0,0 +1,26 @@ +session_name: devnet +before_script: ./setup-devnet.sh +windows: + - layout: tiled + panes: + - shell_command: + - ckb run + - shell_command: + - sleep 3.0 + - ckb miner + - shell_command: + - sleep 3.0 + - ./print_accounts.sh + - shell_command: + - sleep 6.0 + - expect fund_accounts.expect && ckb-cli + - panes: + - shell_command: + - sleep 10.0 + - ./deploy_contracts.sh + - echo "Waiting 15 seconds before funding SUDTs" + - sleep 15.0 + - ./sudt_helper.sh fund + - echo "Waiting 10 seconds before listing SUDT account balances" + - sleep 10.0 + - ./sudt_helper.sh balances diff --git a/payment-channel-ckb/devnet/fund_accounts.expect b/payment-channel-ckb/devnet/fund_accounts.expect new file mode 100644 index 0000000..2389c17 --- /dev/null +++ b/payment-channel-ckb/devnet/fund_accounts.expect @@ -0,0 +1,7 @@ +#!/usr/bin/expect -f + +spawn ./fund_accounts.sh + +expect "Password:" +send "\r" +expect eof diff --git a/payment-channel-ckb/devnet/fund_accounts.sh b/payment-channel-ckb/devnet/fund_accounts.sh new file mode 100755 index 0000000..e404831 --- /dev/null +++ b/payment-channel-ckb/devnet/fund_accounts.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +genesis=$(cat accounts/genesis-1.txt | awk '/testnet/ && !found {print $2; found=1}') +alice=$(cat accounts/alice.txt | awk '/testnet/ && !found {print $2; found=1}') +bob=$(cat accounts/bob.txt | awk '/testnet/ && !found {print $2; found=1}') + +genesis_tx_hash=$(ckb-cli wallet get-live-cells --address $genesis | awk '/tx_hash/ {print $2}') +genesis_tx_index=$(ckb-cli wallet get-live-cells --address $genesis | awk '/output_index/ && !found {print $2; found=1}') +genesis_tx_amount=$(ckb-cli wallet get-live-cells --address $genesis | awk '/capacity/ {print $3}') +FUNDINGTX="fundingtx.json" +FUNDING_AMOUNT=1000 +CHANGE_AMOUNT=$(python -c "print(\"{:.8f}\".format($genesis_tx_amount - 2.0 * 10.0 * $FUNDING_AMOUNT - 1.0))") + +add_output() { + ckb-cli tx add-output --tx-file $FUNDINGTX --to-sighash-address $1 --capacity $2 +} + +ckb-cli tx init --tx-file $FUNDINGTX + +for ((i=1; i <= 10; i++)); do + add_output $alice $FUNDING_AMOUNT +done + +for ((i=1; i <= 10; i++)); do + add_output $bob $FUNDING_AMOUNT +done + +ckb-cli tx add-output --tx-file $FUNDINGTX --to-sighash-address $genesis --capacity $CHANGE_AMOUNT +ckb-cli tx add-input --tx-file $FUNDINGTX --tx-hash $genesis_tx_hash --index $genesis_tx_index +ckb-cli tx sign-inputs --add-signatures --tx-file $FUNDINGTX --from-account $genesis +ckb-cli tx send --tx-file $FUNDINGTX +ckb-cli tx info --tx-file $FUNDINGTX +rm $FUNDINGTX diff --git a/payment-channel-ckb/devnet/print_accounts.sh b/payment-channel-ckb/devnet/print_accounts.sh new file mode 100755 index 0000000..74919c8 --- /dev/null +++ b/payment-channel-ckb/devnet/print_accounts.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +for entry in ./accounts/*; do + echo $entry + account_id=$(cat $entry | awk '/lock_arg:/ { count++; print $2}') + cat $entry + echo $account_id + echo -e '\n' | ckb-cli account export --lock-arg $account_id --extended-privkey-path ${entry%.*}.pk + echo "------------------" +done + +echo "Extract SUDT owner lock-hash into own file" +cat ./accounts/genesis-2.txt | awk '/lock_hash:/ { print $2 }' > ./accounts/sudt-owner-lock-hash.txt diff --git a/payment-channel-ckb/devnet/setup-devnet.sh b/payment-channel-ckb/devnet/setup-devnet.sh new file mode 100755 index 0000000..7625586 --- /dev/null +++ b/payment-channel-ckb/devnet/setup-devnet.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +set -eu +[ -n "${DEBUG:-}" ] && set -x || true + +# This script sets up the devnet for CKB. +# Part of the setup are a miner, two accounts Alice and Bob, as well as the +# registration of two accounts governing the genesis cells. + +ACCOUNTS_DIR="accounts" +PERUN_CONTRACTS_DIR="contracts" + +if [ -d $ACCOUNTS_DIR ]; then + rm -rf $ACCOUNTS_DIR/* +fi +mkdir -p $ACCOUNTS_DIR + +if [ -d "data" ]; then + rm -rf "data" +fi + +if [ -d "specs" ]; then + rm -rf "specs" +fi + +if [ -f "ckb-miner.toml" ]; then + rm "ckb-miner.toml" +fi + +if [ -f "ckb.toml" ]; then + rm "ckb.toml" +fi + +if [ -f "default.db-options" ]; then + rm "default.db-options" +fi + +# Build all required contracts for Perun. +DEVNET=$(pwd) +cd $PERUN_CONTRACTS_DIR +capsule build --release +# If debug contracts are wanted: +# capsule build +cd $DEVNET + +# Genesis cell #1 +GenCellOnePK="0xd00c06bfd800d27397002dca6fb0993d5ba6399b4238b2f29ee9deb97593d2bc" +GenCellOneLockArg="0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7" +GenCellOneAddress="ckt1qyqvsv5240xeh85wvnau2eky8pwrhh4jr8ts8vyj37" +# Genesis cell #2 +GenCellTwoPK="0x63d86723e08f0f813a36ce6aa123bb2289d90680ae1e99d4de8cdb334553f24d" +GenCellTwoLockArg="0x470dcdc5e44064909650113a274b3b36aecb6dc7" +GenCellTwoAddress="ckt1qyqywrwdchjyqeysjegpzw38fvandtktdhrs0zaxl4" + +create_account() { + echo -e '\n\n' | ckb-cli account new > $ACCOUNTS_DIR/$1.txt +} + +# Create accounts for genesis cells. +touch privateKeyGenesisCells.txt +echo $GenCellOnePK > privateKeyGenesisCells.txt +echo -e '\n\n' | ckb-cli account import --privkey-path privateKeyGenesisCells.txt || true +ckb-cli account list | grep -B 5 -A 4 "$GenCellOneAddress" > $ACCOUNTS_DIR/genesis-1.txt +echo $GenCellTwoPK > privateKeyGenesisCells.txt +echo -e '\n\n' | ckb-cli account import --privkey-path privateKeyGenesisCells.txt || true +ckb-cli account list | grep -B 5 -A 4 "$GenCellTwoAddress" > $ACCOUNTS_DIR/genesis-2.txt +rm privateKeyGenesisCells.txt + +echo -e '\n\n' | ckb-cli account new > $ACCOUNTS_DIR/miner.txt +MINER_LOCK_ARG=$(cat $ACCOUNTS_DIR/miner.txt | awk '/lock_arg/ {print $2}') + +create_account "alice" +create_account "bob" + +ckb init --chain dev --ba-arg $MINER_LOCK_ARG --ba-message "0x" --force + +# Make the scripts owned by the miner. +sed -i "s/args =.*$/args = \"$MINER_LOCK_ARG\"/" $PERUN_CONTRACTS_DIR/deployment/dev/deployment.toml +# Use the debug versions of the contracts. +# sed -i "s/release/debug/" $PERUN_CONTRACTS_DIR/deployment/dev/deployment.toml + +# Adjust miner config to process blocks faster. +sed -i 's/value = 5000/value = 1000/' ckb-miner.toml + +# Reduce epoch length to 10 blocks. +sed -i 's/genesis_epoch_length = 1000/genesis_epoch_length = 10/' specs/dev.toml +sed -i '/\[params\]/a\ +max_block_bytes = 100_000_000' specs/dev.toml + +# Enable the indexer. +sed -i '/"Debug"]/ s/"Debug"]/"Debug", "Indexer"]/' ckb.toml +sed -i '/filter = "info"/ s/filter = "info"/filter = "debug"/' ckb.toml +sed -i 's/max_tx_verify_cycles = 70_000_000/max_tx_verify_cycles = 100_000_000/' ckb.toml +# Increase max_request_body_size to allow for debug contracts (large in size) +# to be deployed. +sed -i 's/max_request_body_size =.*$/max_request_body_size = 104857600/' ckb.toml diff --git a/payment-channel-ckb/devnet/sudt-celldep-template.json b/payment-channel-ckb/devnet/sudt-celldep-template.json new file mode 100644 index 0000000..610cf14 --- /dev/null +++ b/payment-channel-ckb/devnet/sudt-celldep-template.json @@ -0,0 +1,17 @@ +{ + "items": { + "sudt": { + "script_id": { + "hash_type": "data1", + "code_hash": "0x" + }, + "cell_dep": { + "out_point": { + "tx_hash": "0x", + "index": "0x0" + }, + "dep_type": "code" + } + } + } +} diff --git a/payment-channel-ckb/devnet/sudt_helper.sh b/payment-channel-ckb/devnet/sudt_helper.sh new file mode 100755 index 0000000..9bffe39 --- /dev/null +++ b/payment-channel-ckb/devnet/sudt_helper.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +ACCOUNTS_DIR="accounts" +SYSTEM_SCRIPTS_DIR="system_scripts" + +# If any of the listed files is missing, exit with an error. +check_files() { + for file in "$@"; do + if [ ! -f "$file" ]; then + echo "File $file not found. Please run $PWD/print_accounts.sh first and" + echo "make sure $PWD/deploy_contracts.sh has been run successfully." + exit 1 + fi + done +} + +check_files "$ACCOUNTS_DIR/alice.txt" "$ACCOUNTS_DIR/bob.txt" "$ACCOUNTS_DIR/genesis-2.txt" "$SYSTEM_SCRIPTS_DIR/sudt-celldep.json" + +ALICE=$(cat $ACCOUNTS_DIR/alice.txt | awk '/testnet/ { count++; if (count == 1) print $2}') +BOB=$(cat $ACCOUNTS_DIR/bob.txt | awk '/testnet/ { count++; if (count == 1) print $2}') +genesis=$(cat $ACCOUNTS_DIR/genesis-2.txt | awk '/testnet/ { count++; if (count == 2) print $2}') +GENESIS=$(cat $ACCOUNTS_DIR/genesis-2.txt | awk '/testnet/ { count++; if (count == 1) print $2}') + +fund_accounts() { + echo "Funding accounts for Alice and Bob with SUDT tokens" + SUDT_AMOUNT=100000000 + + expect << EOF + spawn ckb-cli sudt issue --owner $GENESIS --udt-to $ALICE:$SUDT_AMOUNT $BOB:$SUDT_AMOUNT --cell-deps $SYSTEM_SCRIPTS_DIR/sudt-celldep.json + expect "owner Password:" + send "\r" + expect eof +EOF +} + +list_accounts_balances() { + echo "Listing SUDT account balances for Alice and Bob" + echo "ALICE: ========================================" + ckb-cli sudt get-amount --owner $GENESIS --cell-deps $SYSTEM_SCRIPTS_DIR/sudt-celldep.json --address $ALICE + echo "BOB: ========================================" + ckb-cli sudt get-amount --owner $GENESIS --cell-deps $SYSTEM_SCRIPTS_DIR/sudt-celldep.json --address $BOB + echo "=============================================" +} + +if [ $# -eq 0 ]; then + echo "No arguments provided. Please provide one of the following:" + echo " balances: list SUDT account balances for Alice and Bob" + echo " fund: fund Alice and Bob with SUDT tokens" + exit 1 +fi + +for arg in "$@"; do + case $arg in + balances) + list_accounts_balances + ;; + fund) + fund_accounts + ;; + *) + echo "Unknown argument: $arg" + echo "Usage: $0 [balances|fund]" + exit 1 + ;; + esac +done diff --git a/payment-channel-ckb/go.mod b/payment-channel-ckb/go.mod new file mode 100644 index 0000000..b0077ba --- /dev/null +++ b/payment-channel-ckb/go.mod @@ -0,0 +1,45 @@ +module perun.network/perun-ckb-demo + +go 1.17 + +require ( + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 + github.com/nervosnetwork/ckb-sdk-go/v2 v2.2.0 + github.com/stretchr/testify v1.8.4 + perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e + perun.network/perun-ckb-backend v0.0.0-20240514141411-35bdf3afa166 + polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 +) + +require ( + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Pilatuz/bigz v1.2.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/ethereum/go-ethereum v1.13.10 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/stretchr/objx v0.5.1 // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/tools v0.17.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/nervosnetwork/ckb-sdk-go/v2 v2.2.0 => github.com/perun-network/ckb-sdk-go/v2 v2.2.1-0.20230601140721-2bf596fddd80 diff --git a/payment-channel-ckb/go.sum b/payment-channel-ckb/go.sum new file mode 100644 index 0000000..976b7c6 --- /dev/null +++ b/payment-channel-ckb/go.sum @@ -0,0 +1,1322 @@ +cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= +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.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= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +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/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= +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/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= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +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= +collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= +github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= +github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Pilatuz/bigz v1.2.1 h1:S/R9NGdunq2lIz7KvHu6DmX8bdh41uMqijWeJDz0U5Y= +github.com/Pilatuz/bigz v1.2.1/go.mod h1:FZmplFUEZe3pUr647EQMQgYhV+n9h8+HGTsYK4X6xws= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= +github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes= +github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +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/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= +github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= +github.com/aws/aws-sdk-go-v2/config v1.18.45/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= +github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= +github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= +github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= +github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2/go.mod h1:TQZBt/WaQy+zTHoW++rnl8JBrmZ0VO6EUbVua1+foCA= +github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= +github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= +github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= +github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= +github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +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/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= +github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +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.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= +github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= +github.com/cloudflare/cloudflare-go v0.79.0/go.mod h1:gkHQf9xEubaQPEuerBuoinR9P8bf8a05Lq0X6WKy1Oc= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +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.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= +github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= +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/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-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +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-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +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/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/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= +github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +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= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.9.14/go.mod h1:oP8FC5+TbICUyftkTWs+8JryntjIJLJvWvApK3z2AYw= +github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +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/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= +github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +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/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +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/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +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-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.6/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/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/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.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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/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= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= +github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= +github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= +github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= +github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= +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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/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= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +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/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +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 v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= +github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= +github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= +github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= +github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= +github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= +github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= +github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +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/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/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +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/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= +github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= +github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= +github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +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/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= +github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +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/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.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/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.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= +github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +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/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +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.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.3/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/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/perun-network/ckb-sdk-go/v2 v2.2.1-0.20230601140721-2bf596fddd80 h1:45S239s+5oGVJHakd+BjfZmstG7wHnA8Tkeq43R9eFA= +github.com/perun-network/ckb-sdk-go/v2 v2.2.1-0.20230601140721-2bf596fddd80/go.mod h1:nPpBML8fuaM1NgkKCwv2gSHiCv+xKH43fu8LA9LOQUg= +github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= +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/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +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.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +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.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/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.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +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.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/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.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= +github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +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.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= +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/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+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= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +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/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= +github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= +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.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/testify v1.2.0/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.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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +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.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +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/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +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/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= +github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/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-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +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-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +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/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/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-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-20190327091125-710a502c58a2/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= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/oauth2 v0.0.0-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc= +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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20190813064441-fde4db37ae7a/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-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-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= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210616045830-e2b7044e8c71/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-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/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.2.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.9.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.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +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= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/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-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/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-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +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-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.0/go.mod h1:JWIHJ7U20drSQb/aDpTetJzfC1KlAPldJLpkSy88dvQ= +google.golang.org/api v0.0.0-20170206182103-3d017632ea10/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +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= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +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= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +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/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +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-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.12.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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +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/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +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= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/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/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= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e h1:4SOKO0WZtcsQUwP5nKVUrLUohgUPIhMa8wto5iNCA/k= +perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e/go.mod h1:BGBZC3npkX457u87pjDd0NEIXr1a4dsH4H/YpLdGGe8= +perun.network/perun-ckb-backend v0.0.0-20240514141411-35bdf3afa166 h1:Y/3rPrDpspxiMDkh43sV7lkmbSDTM+m37e66kDEidR0= +perun.network/perun-ckb-backend v0.0.0-20240514141411-35bdf3afa166/go.mod h1:Ebp1GCFpmlSlxuP+fSO9yk/qAllF+6GhRCe7b2533zs= +polycry.pt/poly-go v0.0.0-20220222131629-aa4bdbaab60b/go.mod h1:XUBrNtqgEhN3EEOP/5gh7IBd3xVHKidCjXDZfl9+kMU= +polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 h1:iA5GzEa/hHfVlQpimEjPV09NATwHXxSjWNB0VVodtew= +polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37/go.mod h1:XUBrNtqgEhN3EEOP/5gh7IBd3xVHKidCjXDZfl9+kMU= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +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= diff --git a/payment-channel-ckb/main.go b/payment-channel-ckb/main.go new file mode 100644 index 0000000..447e61d --- /dev/null +++ b/payment-channel-ckb/main.go @@ -0,0 +1,258 @@ +package main + +import ( + "errors" + "fmt" + "io/ioutil" + "log" + "os" + + "github.com/nervosnetwork/ckb-sdk-go/v2/types" + "perun.network/go-perun/channel" + "perun.network/go-perun/channel/persistence/keyvalue" + "perun.network/go-perun/wire" + "perun.network/perun-ckb-backend/channel/asset" + "perun.network/perun-ckb-backend/wallet" + "perun.network/perun-ckb-demo/client" + "perun.network/perun-ckb-demo/deployment" + "polycry.pt/poly-go/sortedkv/memorydb" +) + +const ( + rpcNodeURL = "http://localhost:8114" + Network = types.NetworkTest +) + +func SetLogFile(path string) { + logFile, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatalf("error opening file: %v", err) + } + log.SetOutput(logFile) +} + +func main() { + SetLogFile("demo.log") + sudtOwnerLockArg, err := parseSUDTOwnerLockArg("./devnet/accounts/sudt-owner-lock-hash.txt") + if err != nil { + log.Fatalf("error getting SUDT owner lock arg: %v", err) + } + d, _, err := deployment.GetDeployment("./devnet/contracts/migrations/dev/", "./devnet/system_scripts", sudtOwnerLockArg) + if err != nil { + log.Fatalf("error getting deployment: %v", err) + } + /* + maxSudtCapacity := transaction.CalculateCellCapacity(types.CellOutput{ + Capacity: 0, + Lock: &d.DefaultLockScript, + Type: sudtInfo.Script, + }) + */ + w := wallet.NewEphemeralWallet() + + keyAlice, err := deployment.GetKey("./devnet/accounts/alice.pk") + if err != nil { + log.Fatalf("error getting alice's private key: %v", err) + } + keyBob, err := deployment.GetKey("./devnet/accounts/bob.pk") + if err != nil { + log.Fatalf("error getting bob's private key: %v", err) + } + aliceAccount := wallet.NewAccountFromPrivateKey(keyAlice) + bobAccount := wallet.NewAccountFromPrivateKey(keyBob) + + err = w.AddAccount(aliceAccount) + if err != nil { + log.Fatalf("error adding alice's account: %v", err) + } + err = w.AddAccount(bobAccount) + if err != nil { + log.Fatalf("error adding bob's account: %v", err) + } + + // Setup clients. + log.Println("Setting up clients.") + bus := wire.NewLocalBus() // Message bus used for off-chain communication. + prAlice := keyvalue.NewPersistRestorer(memorydb.NewDatabase()) + prBob := keyvalue.NewPersistRestorer(memorydb.NewDatabase()) + alice, err := client.NewPaymentClient( + "Alice", + Network, + d, + bus, + rpcNodeURL, + aliceAccount, + *keyAlice, + w, + prAlice, + ) + if err != nil { + log.Fatalf("error creating alice's client: %v", err) + } + bob, err := client.NewPaymentClient( + "Bob", + Network, + d, + bus, + rpcNodeURL, + bobAccount, + *keyBob, + w, + prBob, + ) + if err != nil { + log.Fatalf("error creating bob's client: %v", err) + } + + //print balances before transaction + + fmt.Println("Balances of Alice and Bob before transaction") + str := "'s account balance" + fmt.Println(alice.Name, str, alice.GetBalances()) + fmt.Println(bob.Name, str, bob.GetBalances()) + + ckbAsset := asset.Asset{ + IsCKBytes: true, + SUDT: nil, + } + + /* + sudtAsset := asset.Asset{ + IsCKBytes: false, + SUDT: &asset.SUDT{ + TypeScript: *sudtInfo.Script, + MaxCapacity: maxSudtCapacity, + }, + } + */ + + fmt.Println("Opening channel and depositing funds") + chAlice := alice.OpenChannel(bob.WireAddress(), map[channel.Asset]float64{ + &asset.Asset{ + IsCKBytes: true, + SUDT: nil, + }: 100.0, + }) + strAlice := "Alice" + strBob := "Bob" + fmt.Println(alice.Name, str, alice.GetBalances()) + fmt.Println(bob.Name, str, bob.GetBalances()) + + fmt.Println("Alice sent proposal") + chBob := bob.AcceptedChannel() + fmt.Println("Bob accepted proposal") + fmt.Println("Sending payments....") + + chAlice.SendPayment(map[channel.Asset]float64{ + &ckbAsset: 10.0, + }) + fmt.Println("Alice sent Bob a payment") + printAllocationBalances(chAlice, ckbAsset, strAlice) + printAllocationBalances(chBob, ckbAsset, strBob) + + chBob.SendPayment(map[channel.Asset]float64{ + &ckbAsset: 10.0, + }) + fmt.Println("Bob sent Alice a payment") + printAllocationBalances(chAlice, ckbAsset, strAlice) + printAllocationBalances(chBob, ckbAsset, strBob) + + chAlice.SendPayment(map[channel.Asset]float64{ + &ckbAsset: 10.0, + }) + fmt.Println("Alice sent Bob a payment") + printAllocationBalances(chAlice, ckbAsset, strAlice) + printAllocationBalances(chBob, ckbAsset, strBob) + + fmt.Println("Payments completed") + printAllocationBalances(chAlice, ckbAsset, strAlice) + printAllocationBalances(chBob, ckbAsset, strBob) + + fmt.Println("Skip Settling Channel and force client shutdown") + //chAlice.Settle() + + fmt.Println(alice.Name, str, alice.GetBalances()) + fmt.Println(bob.Name, str, bob.GetBalances()) + + //cleanup + alice.Shutdown() + bob.Shutdown() + fmt.Println("Clients shutdown, exiting method") + + fmt.Println("Creating clients again to see if channels can be restored") + alice2, err := client.NewPaymentClient( + "Alice", + Network, + d, + bus, + rpcNodeURL, + aliceAccount, + *keyAlice, + w, + prAlice, + ) + if err != nil { + log.Fatalf("error creating alice's client: %v", err) + } + bob2, err := client.NewPaymentClient( + "Bob", + Network, + d, + bus, + rpcNodeURL, + bobAccount, + *keyBob, + w, + prBob, + ) + if err != nil { + log.Fatalf("error creating bob's client: %v", err) + } + + chansAlice := alice2.Restore() + chansBob := bob2.Restore() + fmt.Println("Alice and Bob's channels successfully restored") + + // Print balances after transactions. + fmt.Println(alice.Name, str, alice.GetBalances()) + fmt.Println(bob.Name, str, bob.GetBalances()) + + fmt.Println("Alice sending payment to Bob") + chansAlice[0].SendPayment(map[channel.Asset]float64{ + &ckbAsset: 10.0, + }) + fmt.Println("Bob sending payment to Alice") + chansBob[0].SendPayment(map[channel.Asset]float64{ + &ckbAsset: 10.0, + }) + + chansAlice[0].Settle() + fmt.Println("Balances after settling channel") + fmt.Println(alice.Name, str, alice.GetBalances()) + fmt.Println(bob.Name, str, bob.GetBalances()) + +} + +func printAllocationBalances(ch *client.PaymentChannel, asset asset.Asset, name string) { + chAlloc := ch.State().Allocation + //_assets := chAlloc.Assets + fmt.Println("Assets held by" + name) + /* + for _, a := range _assets { + fmt.Println(a) + } + */ + fmt.Println(name + "'s allocation in channel: " + chAlloc.Balance(1, &asset).String()) +} + +func parseSUDTOwnerLockArg(pathToSUDTOwnerLockArg string) (string, error) { + b, err := ioutil.ReadFile(pathToSUDTOwnerLockArg) + if err != nil { + return "", fmt.Errorf("reading sudt owner lock arg from file: %w", err) + } + sudtOwnerLockArg := string(b) + if sudtOwnerLockArg == "" { + return "", errors.New("sudt owner lock arg not found in file") + } + return sudtOwnerLockArg, nil +} From 19d47e6be76dc9f4989a505ecfc7cd74a13a047f Mon Sep 17 00:00:00 2001 From: ravi0131 Date: Tue, 14 May 2024 19:14:45 +0200 Subject: [PATCH 2/4] wire discovery added --- payment-channel-ckb/client/client.go | 51 ++-- payment-channel-ckb/go.mod | 75 ++++- payment-channel-ckb/go.sum | 438 ++++++++++++++++++++++++++- payment-channel-ckb/main.go | 49 ++- 4 files changed, 575 insertions(+), 38 deletions(-) diff --git a/payment-channel-ckb/client/client.go b/payment-channel-ckb/client/client.go index 90d62d8..7564e30 100644 --- a/payment-channel-ckb/client/client.go +++ b/payment-channel-ckb/client/client.go @@ -9,6 +9,7 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/nervosnetwork/ckb-sdk-go/v2/rpc" "github.com/nervosnetwork/ckb-sdk-go/v2/types" + "github.com/perun-network/perun-libp2p-wire/p2p" "perun.network/go-perun/channel" gpchannel "perun.network/go-perun/channel" "perun.network/go-perun/channel/persistence" @@ -16,7 +17,6 @@ import ( gpwallet "perun.network/go-perun/wallet" "perun.network/go-perun/watcher/local" "perun.network/go-perun/wire" - "perun.network/go-perun/wire/net/simple" "perun.network/perun-ckb-backend/backend" "perun.network/perun-ckb-backend/channel/adjudicator" "perun.network/perun-ckb-backend/channel/asset" @@ -36,21 +36,23 @@ type PaymentClient struct { wAddr wire.Address Network types.Network PerunClient *client.Client - - channels chan *PaymentChannel - rpcClient rpc.Client + net *p2p.Net + channels chan *PaymentChannel + rpcClient rpc.Client } func NewPaymentClient( name string, network types.Network, deployment backend.Deployment, - bus wire.Bus, rpcUrl string, account *wallet.Account, key secp256k1.PrivateKey, wallet *wallet.EphemeralWallet, persistRestorer persistence.PersistRestorer, + wAddr wire.Address, + net *p2p.Net, + ) (*PaymentClient, error) { backendRPCClient, err := rpc.Dial(rpcUrl) if err != nil { @@ -68,8 +70,19 @@ func NewPaymentClient( if err != nil { return nil, err } - wAddr := simple.NewAddress(account.Address().String()) - perunClient, err := client.New(wAddr, bus, f, a, wallet, watcher) + /* + wireAcc := p2p.NewRandomAccount(rand.New(rand.NewSource(time.Now().UnixNano()))) + net, err := p2p.NewP2PBus(wireAcc) + if err != nil { + panic(errors.Wrap(err, "creating p2p net")) + } + bus := net.Bus + listener := net.Listener + + //wAddr := simple.NewAddress(account.Address().String()) + wAddr := wireAcc.Address() + */ + perunClient, err := client.New(wAddr, net.Bus, f, a, wallet, watcher) if err != nil { return nil, err } @@ -89,6 +102,7 @@ func NewPaymentClient( PerunClient: perunClient, channels: make(chan *PaymentChannel, 1), rpcClient: balanceRPC, + net: net, } //go p.PollBalances() @@ -105,6 +119,11 @@ func (p *PaymentClient) WireAddress() wire.Address { return p.wAddr } +func (p *PaymentClient) PeerID() string { + walletAddr := p.wAddr.(*p2p.Address) + return walletAddr.ID.String() +} + func (p *PaymentClient) GetSudtBalance() *big.Int { p.balanceMutex.Lock() defer p.balanceMutex.Unlock() @@ -126,12 +145,13 @@ func (p *PaymentClient) GetBalances() string { } // OpenChannel opens a new channel with the specified peer and funding. -func (p *PaymentClient) OpenChannel(peer wire.Address, amounts map[gpchannel.Asset]float64) *PaymentChannel { +func (p *PaymentClient) OpenChannel(peer wire.Address, peerID string, amounts map[gpchannel.Asset]float64) *PaymentChannel { // We define the channel participants. The proposer always has index 0. Here // we use the on-chain addresses as off-chain addresses, but we could also // use different ones. log.Println("OpenChannel called") participants := []wire.Address{p.WireAddress(), peer} + p.net.Dialer.Register(peer, peerID) assets := make([]gpchannel.Asset, len(amounts)) i := 0 @@ -214,22 +234,11 @@ func (p *PaymentClient) Shutdown() { p.PerunClient.Close() } -func (c *PaymentClient) Restore() []*PaymentChannel { +func (c *PaymentClient) Restore(peer wire.Address, peerID string) []*PaymentChannel { var restoredChannels []*client.Channel - + //c.net.Dialer.Register(peer, peerID) //TODO: Remove this hack. Find why asset is not found upon restoring c.PerunClient.OnNewChannel(func(ch *client.Channel) { - /* - state := ch.State().Clone() - ckbyte := asset.Asset{ - IsCKBytes: true, - SUDT: nil, - } - //create a new allocation where asset type is defined - alloc := gpchannel.NewAllocation(2, ckbyte) - ckbBalances := state.Allocation.Balances[0] - alloc.SetAssetBalances(ckbyte, ckbBalances) - */ restoredChannels = append(restoredChannels, ch) }) diff --git a/payment-channel-ckb/go.mod b/payment-channel-ckb/go.mod index b0077ba..0907bf1 100644 --- a/payment-channel-ckb/go.mod +++ b/payment-channel-ckb/go.mod @@ -5,7 +5,8 @@ go 1.17 require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/nervosnetwork/ckb-sdk-go/v2 v2.2.0 - github.com/stretchr/testify v1.8.4 + github.com/perun-network/perun-libp2p-wire v0.0.0-20240514121025-635388735967 + github.com/stretchr/testify v1.9.0 perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e perun.network/perun-ckb-backend v0.0.0-20240514141411-35bdf3afa166 polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 @@ -14,24 +15,92 @@ require ( require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Pilatuz/bigz v1.2.1 // indirect + github.com/btcsuite/btcd v0.20.1-beta // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/ethereum/go-ethereum v1.13.10 // indirect + github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gopacket v1.1.17 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/holiman/uint256 v1.2.4 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/ipfs/go-cid v0.0.7 // indirect + github.com/ipfs/go-ipfs-util v0.0.2 // indirect + github.com/ipfs/go-log v1.0.4 // indirect + github.com/ipfs/go-log/v2 v2.1.1 // 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/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d // indirect + github.com/libp2p/go-addr-util v0.0.2 // indirect + github.com/libp2p/go-buffer-pool v0.0.2 // indirect + github.com/libp2p/go-conn-security-multistream v0.2.0 // indirect + github.com/libp2p/go-eventbus v0.2.1 // indirect + github.com/libp2p/go-flow-metrics v0.0.3 // indirect + github.com/libp2p/go-libp2p v0.13.0 // indirect + github.com/libp2p/go-libp2p-autonat v0.4.0 // indirect + github.com/libp2p/go-libp2p-blankhost v0.2.0 // indirect + github.com/libp2p/go-libp2p-circuit v0.4.0 // indirect + github.com/libp2p/go-libp2p-core v0.8.5 // indirect + github.com/libp2p/go-libp2p-discovery v0.5.0 // indirect + github.com/libp2p/go-libp2p-loggables v0.1.0 // indirect + github.com/libp2p/go-libp2p-mplex v0.4.1 // indirect + github.com/libp2p/go-libp2p-nat v0.0.6 // indirect + github.com/libp2p/go-libp2p-noise v0.1.1 // indirect + github.com/libp2p/go-libp2p-peerstore v0.2.6 // indirect + github.com/libp2p/go-libp2p-pnet v0.2.0 // indirect + github.com/libp2p/go-libp2p-swarm v0.4.0 // indirect + github.com/libp2p/go-libp2p-tls v0.1.3 // indirect + github.com/libp2p/go-libp2p-transport-upgrader v0.4.0 // indirect + github.com/libp2p/go-libp2p-yamux v0.5.1 // indirect + github.com/libp2p/go-mplex v0.3.0 // indirect + github.com/libp2p/go-msgio v0.0.6 // indirect + github.com/libp2p/go-nat v0.0.5 // indirect + github.com/libp2p/go-netroute v0.1.3 // indirect + github.com/libp2p/go-openssl v0.0.7 // indirect + github.com/libp2p/go-reuseport v0.0.2 // indirect + github.com/libp2p/go-reuseport-transport v0.0.4 // indirect + github.com/libp2p/go-sockaddr v0.0.2 // indirect + github.com/libp2p/go-stream-muxer-multistream v0.3.0 // indirect + github.com/libp2p/go-tcp-transport v0.2.1 // indirect + github.com/libp2p/go-ws-transport v0.4.0 // indirect + github.com/libp2p/go-yamux/v2 v2.0.0 // indirect github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect + github.com/minio/sha256-simd v0.1.1 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.0.3 // indirect + github.com/multiformats/go-base36 v0.1.0 // indirect + github.com/multiformats/go-multiaddr v0.3.3 // indirect + github.com/multiformats/go-multiaddr-dns v0.2.0 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multiaddr-net v0.2.0 // indirect + github.com/multiformats/go-multibase v0.0.3 // indirect + github.com/multiformats/go-multihash v0.0.14 // indirect + github.com/multiformats/go-multistream v0.2.0 // indirect + github.com/multiformats/go-varint v0.0.6 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/stretchr/objx v0.5.1 // indirect + github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect + github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opencensus.io v0.22.4 // indirect + go.uber.org/atomic v1.6.0 // indirect + go.uber.org/multierr v1.5.0 // indirect + go.uber.org/zap v1.15.0 // indirect golang.org/x/crypto v0.18.0 // indirect golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect golang.org/x/mod v0.14.0 // indirect diff --git a/payment-channel-ckb/go.sum b/payment-channel-ckb/go.sum index 976b7c6..65a8e8c 100644 --- a/payment-channel-ckb/go.sum +++ b/payment-channel-ckb/go.sum @@ -37,6 +37,7 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= @@ -67,9 +68,11 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= @@ -80,6 +83,7 @@ github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= +github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -93,6 +97,7 @@ github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNu github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes= github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -141,14 +146,26 @@ github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+Wji github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -188,6 +205,8 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE 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/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 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= @@ -200,9 +219,12 @@ github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXk github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 h1:6xT9KW8zLC5IlbaIF5Q7JNieBoACT7iW0YTxQHR0in0= +github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= @@ -217,9 +239,14 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etly github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= +github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= +github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -262,10 +289,13 @@ github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6Ytix github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= +github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 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/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= @@ -322,7 +352,9 @@ github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q8 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/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.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= @@ -334,8 +366,10 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= 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 h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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= @@ -345,6 +379,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -389,12 +424,15 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= +github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 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/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -411,6 +449,8 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/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/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= 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= @@ -418,6 +458,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= @@ -427,6 +468,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg 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/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= +github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= @@ -435,7 +478,9 @@ github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5 github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 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 v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= @@ -446,6 +491,7 @@ github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= @@ -466,19 +512,70 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= +github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= +github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +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-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= +github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= +github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= +github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= +github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= +github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= +github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= +github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= +github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= +github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= +github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= +github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= +github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= +github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= +github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= +github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= +github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= +github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= +github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= +github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +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/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= +github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= +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.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= +github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= +github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= +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/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 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= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -497,6 +594,7 @@ github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= @@ -508,6 +606,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -518,8 +617,11 @@ github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPR github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ= +github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= 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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -535,12 +637,193 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= +github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= +github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= +github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M= +github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= +github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= +github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc= +github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= +github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= +github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= +github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= +github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= +github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k= +github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= +github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= +github.com/libp2p/go-libp2p v0.13.0 h1:tDdrXARSghmusdm0nf1U/4M8aj8Rr0V2IzQOXmbzQ3s= +github.com/libp2p/go-libp2p v0.13.0/go.mod h1:pM0beYdACRfHO1WcJlp65WXyG2A6NqYM+t2DTVAJxMo= +github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= +github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= +github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= +github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= +github.com/libp2p/go-libp2p-autonat v0.4.0 h1:3y8XQbpr+ssX8QfZUHekjHCYK64sj6/4hnf/awD4+Ug= +github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= +github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= +github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= +github.com/libp2p/go-libp2p-blankhost v0.2.0 h1:3EsGAi0CBGcZ33GwRuXEYJLLPoVWyXJ1bcJzAJjINkk= +github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= +github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= +github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= +github.com/libp2p/go-libp2p-circuit v0.4.0 h1:eqQ3sEYkGTtybWgr6JLqJY6QLtPWRErvFjFDfAOO1wc= +github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= +github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= +github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= +github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= +github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= +github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= +github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= +github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= +github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= +github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= +github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= +github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= +github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= +github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= +github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= +github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= +github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= +github.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+WgN9mN0iWviQ= +github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= +github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= +github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= +github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= +github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= +github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= +github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= +github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= +github.com/libp2p/go-libp2p-mplex v0.4.1 h1:/pyhkP1nLwjG3OM+VuaNJkQT/Pqq73WzB3aDN3Fx1sc= +github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= +github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= +github.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU= +github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= +github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= +github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= +github.com/libp2p/go-libp2p-noise v0.1.1 h1:vqYQWvnIcHpIoWJKC7Al4D6Hgj0H012TuXRhPwSMGpQ= +github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM= +github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= +github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= +github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= +github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= +github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= +github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= +github.com/libp2p/go-libp2p-peerstore v0.2.6 h1:2ACefBX23iMdJU9Ke+dcXt3w86MIryes9v7In4+Qq3U= +github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= +github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= +github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= +github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= +github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= +github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= +github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= +github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= +github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= +github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= +github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= +github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= +github.com/libp2p/go-libp2p-swarm v0.4.0 h1:hahq/ijRoeH6dgROOM8x7SeaKK5VgjjIr96vdrT+NUA= +github.com/libp2p/go-libp2p-swarm v0.4.0/go.mod h1:XVFcO52VoLoo0eitSxNQWYq4D6sydGOweTOAjJNraCw= +github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= +github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= +github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= +github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= +github.com/libp2p/go-libp2p-testing v0.4.0 h1:PrwHRi0IGqOwVQWR3xzgigSlhlLfxgfXgkHxr77EghQ= +github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= +github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM= +github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= +github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= +github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= +github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.0 h1:xwj4h3hJdBrxqMOyMUjwscjoVst0AASTsKtZiTChoHI= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.0/go.mod h1:J4ko0ObtZSmgn5BX5AmegP+dK3CSnU2lMCKsSq/EY0s= +github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= +github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= +github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= +github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= +github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= +github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= +github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= +github.com/libp2p/go-libp2p-yamux v0.5.1 h1:sX4WQPHMhRxJE5UZTfjEuBvlQWXB5Bo3A2JK9ZJ9EM0= +github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4= +github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= +github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= +github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= +github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= +github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= +github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= +github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= +github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= +github.com/libp2p/go-mplex v0.3.0 h1:U1T+vmCYJaEoDJPV1aq31N56hS+lJgb397GsylNSgrU= +github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= +github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-msgio v0.0.6 h1:lQ7Uc0kS1wb1EfRxO2Eir/RJoHkHn7t6o+EiwsYIKJA= +github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= +github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= +github.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q= +github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= +github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-netroute v0.1.3 h1:1ngWRx61us/EpaKkdqkMjKk/ufr/JlIFYQAxV2XX8Ig= +github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= +github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= +github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= +github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU= +github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= +github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= +github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= +github.com/libp2p/go-reuseport-transport v0.0.4 h1:OZGz0RB620QDGpv300n1zaOcKGGAoGVf8h9txtt/1uM= +github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= +github.com/libp2p/go-sockaddr v0.0.2 h1:tCuXfpA9rq7llM/v834RKc/Xvovy/AqM9kHvTV/jY/Q= +github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= +github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= +github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY= +github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= +github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= +github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= +github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= +github.com/libp2p/go-tcp-transport v0.2.1 h1:ExZiVQV+h+qL16fzCWtd1HSzPsqWottJ8KXwWaVi8Ns= +github.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M= +github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= +github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= +github.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k= +github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= +github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI= +github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux/v2 v2.0.0 h1:vSGhAy5u6iHBq11ZDcyHH4Blcf9xlBhT4WQDoOE90LU= +github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.0/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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -552,6 +835,7 @@ github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HN github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= @@ -572,9 +856,19 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -591,7 +885,64 @@ github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3P github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +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/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= +github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= +github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= +github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= +github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= +github.com/multiformats/go-multiaddr v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs= +github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= +github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= +github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +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-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= +github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= +github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= +github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= +github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= +github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= +github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk= +github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= +github.com/multiformats/go-multistream v0.2.0 h1:6AuNmQVKUkRnddw2YiDjt5Elit40SFxMJkVnhmETXtU= +github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +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/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/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= @@ -600,6 +951,7 @@ github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -607,21 +959,33 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 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.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 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.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +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/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/perun-network/ckb-sdk-go/v2 v2.2.1-0.20230601140721-2bf596fddd80 h1:45S239s+5oGVJHakd+BjfZmstG7wHnA8Tkeq43R9eFA= github.com/perun-network/ckb-sdk-go/v2 v2.2.1-0.20230601140721-2bf596fddd80/go.mod h1:nPpBML8fuaM1NgkKCwv2gSHiCv+xKH43fu8LA9LOQUg= +github.com/perun-network/perun-libp2p-wire v0.0.0-20240514121025-635388735967 h1:m+63gl1KMpLdseyNEKLnUFrCYipD9Q0Cnf/hmOwAkhs= +github.com/perun-network/perun-libp2p-wire v0.0.0-20240514121025-635388735967/go.mod h1:kA/iWKN+2BvzxuUllMmd1IY6H2oKUZ18U+60hrY19EM= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= @@ -705,8 +1069,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= 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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -718,6 +1088,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn 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/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= @@ -726,24 +1097,28 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +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 v1.2.0/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.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= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 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.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= 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= @@ -772,8 +1147,16 @@ github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBn github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= +github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= +github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= +github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= +github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= +github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -797,26 +1180,47 @@ github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +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-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -872,8 +1276,10 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -901,6 +1307,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r 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-20190227160552-c95aed5357e7/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-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -913,6 +1320,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -986,12 +1394,16 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h 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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/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= @@ -999,6 +1411,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/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-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1099,6 +1512,7 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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= @@ -1110,6 +1524,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/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-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/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-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1129,12 +1544,16 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1171,7 +1590,9 @@ golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= @@ -1208,6 +1629,7 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/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-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1250,9 +1672,12 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 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= @@ -1282,6 +1707,9 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= +gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= +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/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1292,6 +1720,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1303,10 +1732,13 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh 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= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 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= +honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +perun.network/go-perun v0.10.6/go.mod h1:BGBZC3npkX457u87pjDd0NEIXr1a4dsH4H/YpLdGGe8= perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e h1:4SOKO0WZtcsQUwP5nKVUrLUohgUPIhMa8wto5iNCA/k= perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e/go.mod h1:BGBZC3npkX457u87pjDd0NEIXr1a4dsH4H/YpLdGGe8= perun.network/perun-ckb-backend v0.0.0-20240514141411-35bdf3afa166 h1:Y/3rPrDpspxiMDkh43sV7lkmbSDTM+m37e66kDEidR0= diff --git a/payment-channel-ckb/main.go b/payment-channel-ckb/main.go index 447e61d..a5db617 100644 --- a/payment-channel-ckb/main.go +++ b/payment-channel-ckb/main.go @@ -5,12 +5,14 @@ import ( "fmt" "io/ioutil" "log" + "math/rand" "os" + "time" "github.com/nervosnetwork/ckb-sdk-go/v2/types" + "github.com/perun-network/perun-libp2p-wire/p2p" "perun.network/go-perun/channel" "perun.network/go-perun/channel/persistence/keyvalue" - "perun.network/go-perun/wire" "perun.network/perun-ckb-backend/channel/asset" "perun.network/perun-ckb-backend/wallet" "perun.network/perun-ckb-demo/client" @@ -70,35 +72,57 @@ func main() { log.Fatalf("error adding bob's account: %v", err) } + aliceWireAcc := p2p.NewRandomAccount(rand.New(rand.NewSource(time.Now().UnixNano()))) + aliceNet, err := p2p.NewP2PBus(aliceWireAcc) + if err != nil { + log.Fatalf("creating p2p net", err) + } + aliceBus := aliceNet.Bus + aliceListener := aliceNet.Listener + go aliceBus.Listen(aliceListener) + // Setup clients. log.Println("Setting up clients.") - bus := wire.NewLocalBus() // Message bus used for off-chain communication. + //bus := wire.NewLocalBus() // Message bus used for off-chain communication. prAlice := keyvalue.NewPersistRestorer(memorydb.NewDatabase()) - prBob := keyvalue.NewPersistRestorer(memorydb.NewDatabase()) + alice, err := client.NewPaymentClient( "Alice", Network, d, - bus, rpcNodeURL, aliceAccount, *keyAlice, w, prAlice, + aliceWireAcc.Address(), + aliceNet, ) if err != nil { log.Fatalf("error creating alice's client: %v", err) } + + prBob := keyvalue.NewPersistRestorer(memorydb.NewDatabase()) + bobWireAcc := p2p.NewRandomAccount(rand.New(rand.NewSource(time.Now().UnixNano()))) + bobNet, err := p2p.NewP2PBus(bobWireAcc) + if err != nil { + log.Fatalf("creating p2p net", err) + } + bobBus := bobNet.Bus + bobListener := bobNet.Listener + go bobBus.Listen(bobListener) + bob, err := client.NewPaymentClient( "Bob", Network, d, - bus, rpcNodeURL, bobAccount, *keyBob, w, prBob, + bobWireAcc.Address(), + bobNet, ) if err != nil { log.Fatalf("error creating bob's client: %v", err) @@ -127,7 +151,7 @@ func main() { */ fmt.Println("Opening channel and depositing funds") - chAlice := alice.OpenChannel(bob.WireAddress(), map[channel.Asset]float64{ + chAlice := alice.OpenChannel(bob.WireAddress(), bob.PeerID(), map[channel.Asset]float64{ &asset.Asset{ IsCKBytes: true, SUDT: nil, @@ -184,12 +208,13 @@ func main() { "Alice", Network, d, - bus, rpcNodeURL, aliceAccount, *keyAlice, w, prAlice, + alice.WireAddress(), + aliceNet, ) if err != nil { log.Fatalf("error creating alice's client: %v", err) @@ -198,19 +223,21 @@ func main() { "Bob", Network, d, - bus, rpcNodeURL, bobAccount, *keyBob, w, prBob, + bob.WireAddress(), + bobNet, ) if err != nil { log.Fatalf("error creating bob's client: %v", err) } - - chansAlice := alice2.Restore() - chansBob := bob2.Restore() + fmt.Println("Starting restoring channels") + chansAlice := alice2.Restore(bob2.WireAddress(), bob2.PeerID()) + fmt.Println("Alice's channel restored") + chansBob := bob2.Restore(alice2.WireAddress(), alice2.PeerID()) fmt.Println("Alice and Bob's channels successfully restored") // Print balances after transactions. From a889f411ce89cbd7af5ff9218b3c3d97a3ddbdb0 Mon Sep 17 00:00:00 2001 From: ravi0131 Date: Tue, 9 Jul 2024 14:23:48 +0200 Subject: [PATCH 3/4] feat: add restore function to paymentClient && chore: cleanup - add Restore method to PaymentClient in client.go - update .gitignore and remove unecessary .idea files --- payment-channel-ckb/.gitignore | 1 + payment-channel-ckb/.idea/.gitignore | 8 ---- payment-channel-ckb/.idea/modules.xml | 8 ---- .../.idea/payment-channel-ckb.iml | 9 ----- payment-channel-ckb/.idea/vcs.xml | 6 --- payment-channel/client/client.go | 38 +++++++++++++++---- 6 files changed, 31 insertions(+), 39 deletions(-) delete mode 100644 payment-channel-ckb/.idea/.gitignore delete mode 100644 payment-channel-ckb/.idea/modules.xml delete mode 100644 payment-channel-ckb/.idea/payment-channel-ckb.iml delete mode 100644 payment-channel-ckb/.idea/vcs.xml diff --git a/payment-channel-ckb/.gitignore b/payment-channel-ckb/.gitignore index 146c59d..4f08510 100644 --- a/payment-channel-ckb/.gitignore +++ b/payment-channel-ckb/.gitignore @@ -25,3 +25,4 @@ devnet/ckb-miner.toml devnet/ckb.toml devnet/default.db-options devnet/system_scripts +.vscode diff --git a/payment-channel-ckb/.idea/.gitignore b/payment-channel-ckb/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/payment-channel-ckb/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/payment-channel-ckb/.idea/modules.xml b/payment-channel-ckb/.idea/modules.xml deleted file mode 100644 index 8b4f43e..0000000 --- a/payment-channel-ckb/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/payment-channel-ckb/.idea/payment-channel-ckb.iml b/payment-channel-ckb/.idea/payment-channel-ckb.iml deleted file mode 100644 index 5e764c4..0000000 --- a/payment-channel-ckb/.idea/payment-channel-ckb.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/payment-channel-ckb/.idea/vcs.xml b/payment-channel-ckb/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/payment-channel-ckb/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/payment-channel/client/client.go b/payment-channel/client/client.go index 7ef0cd8..4b22272 100644 --- a/payment-channel/client/client.go +++ b/payment-channel/client/client.go @@ -17,20 +17,20 @@ package client import ( "context" "fmt" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" "math/big" - ethchannel "perun.network/go-perun/backend/ethereum/channel" ethwallet "perun.network/go-perun/backend/ethereum/wallet" swallet "perun.network/go-perun/backend/ethereum/wallet/simple" + "perun.network/go-perun/channel/persistence" + "perun.network/go-perun/watcher/local" + "perun.network/go-perun/channel" "perun.network/go-perun/client" "perun.network/go-perun/wallet" - "perun.network/go-perun/watcher/local" "perun.network/go-perun/wire" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" ) const ( @@ -54,6 +54,7 @@ func SetupPaymentClient( chainID uint64, // chainID is the identifier of the blockchain. adjudicator common.Address, // adjudicator is the address of the adjudicator. asset ethwallet.Address, // asset is the address of the asset holder for our payment channels. + pr persistence.PersistRestorer, ) (*PaymentClient, error) { // Create Ethereum client and contract backend. cb, err := CreateContractBackend(nodeURL, chainID, w) @@ -74,11 +75,11 @@ func SetupPaymentClient( // Setup funder. funder := ethchannel.NewFunder(cb) dep := ethchannel.NewETHDepositor() - ethAcc := accounts.Account{Address: acc} + ethAcc := accounts.Account{Address: acc} // Account to be used for funding transactions funder.RegisterAsset(asset, dep, ethAcc) // Setup adjudicator. - adj := ethchannel.NewAdjudicator(cb, adjudicator, acc, ethAcc) + adj := ethchannel.NewAdjudicator(cb, adjudicator, acc, ethAcc) // acc is the address of the account that will receive the payout // Setup dispute watcher. watcher, err := local.NewWatcher(adj) @@ -89,6 +90,7 @@ func SetupPaymentClient( // Setup Perun client. waddr := ethwallet.AsWalletAddr(acc) perunClient, err := client.New(waddr, bus, funder, adj, w, watcher) + perunClient.EnablePersistence(pr) if err != nil { return nil, errors.WithMessage(err, "creating client") } @@ -162,3 +164,23 @@ func (c *PaymentClient) AcceptedChannel() *PaymentChannel { func (c *PaymentClient) Shutdown() { c.perunClient.Close() } + +func (c *PaymentClient) Restore() []*PaymentChannel { + var restoredChannels []*client.Channel + + c.perunClient.OnNewChannel(func(ch *client.Channel) { + restoredChannels = append(restoredChannels, ch) + }) + + err := c.perunClient.Restore(context.TODO()) + if err != nil { + fmt.Println("Error restoring channels") + } + + paymentChannels := make([]*PaymentChannel, len(restoredChannels)) + for i, ch := range restoredChannels { + paymentChannels[i] = newPaymentChannel(ch, c.currency) + } + + return paymentChannels +} From 6e78a06b3f0e5524354bfcdf192a13eb819e96aa Mon Sep 17 00:00:00 2001 From: ravi0131 Date: Thu, 17 Oct 2024 13:14:29 +0200 Subject: [PATCH 4/4] chore: update .gitignore --- payment-channel-ckb/.gitignore | 2 +- payment-channel-ckb/.vscode/launch.json | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 payment-channel-ckb/.vscode/launch.json diff --git a/payment-channel-ckb/.gitignore b/payment-channel-ckb/.gitignore index 4f08510..336fc31 100644 --- a/payment-channel-ckb/.gitignore +++ b/payment-channel-ckb/.gitignore @@ -25,4 +25,4 @@ devnet/ckb-miner.toml devnet/ckb.toml devnet/default.db-options devnet/system_scripts -.vscode +.vscode/ diff --git a/payment-channel-ckb/.vscode/launch.json b/payment-channel-ckb/.vscode/launch.json deleted file mode 100644 index 2a17be5..0000000 --- a/payment-channel-ckb/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - - { - "name": "Launch Package", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "${fileDirname}" - } - - ] -} \ No newline at end of file